lynaghk / cljx

Write a portable codebase targeting Clojure/ClojureScript

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Circular dependency bug when user.clj exists

weavejester opened this issue · comments

When lein cljx once or lein cljx auto is executed, any code in the user namespace is automatically loaded. In the development profile, this namespace may be populated. For instance if a project is designed along the lines of Stuart Sierra's reloaded workflow, there may be a file "dev/user.clj" that is included in the development profile.

If this is the case, running lein cljx can result in a circular dependency, if the user namespace depends on files that are generated by cljx.

I'm currently working around this with the following:

:aliases {"cljx" ["with-profile" "cljx" "cljx"]}
:profiles {:cljx {}}

That probably can't be solved as user.clj is loaded by the Clojure runtime. See #20 for some related info.

Having a workaround might be the best that can be done. Maybe something to add to the documentation?

@weavejester Just to make sure I understand what that workaround is doing for you: :cljx is empty, and so disables the :dev profile, and thus your user.clj (which is only on the classpath because of some configuration in the :dev profile) is not visible when cljx runs?

user.clj is a strange beast in general because of the stage at which it is loaded. May I suggest putting code that you might otherwise add to user.clj into e.g. :repl-options :init or :inclusions?

@weavejester I use Sierra's reloaded pattern all the time, and I've run into the same issue and come to the same solution.
Do you think there's a neater way to workaround?
Or are you suggesting that we simply add a note about this to the README somewhere?

@cemerick, I usually have options set up like:

:profiles {:dev {:source-paths ["src" "dev"]}}

And then place dev-specific source files in a dev directory, such as dev/user.clj.

Obviously this causes problems for cljx, so my solution is:

:aliases {"cljx" ["with-profile" "cljx" "cljx"]}
:profiles {:dev {:source-paths ["src" "dev"]}
           :cljx {}}

I wonder if it would be possible to have lein cljx automatically merge in a :cljx profile, in the same way that lein uberjar automatically merges in a :uberjar profile.

I'm not sure whether that would solve it completely, but it would at least remove the need for an alias.

Merging an empty profile means that e.g. :dev :depenencies aren't available, and so custom :rules provided by deps wouldn't be accessible.

This should work; prepend to your user.clj:

(require '[cljx.repl-middleware :as cljx])
(reset! @#'cljx/cljx-load-rules {:clj cljx.rules/clj-rules})
@@#'cljx/install-cljx-load

It's so hairy-looking because those bits are currently intended solely for use by the nREPL middleware (thus the ns name), but they accomplish the same thing here: swapping in an enhanced clojure.core/load that knows how to resolve, transform, and load .cljx files.

I hadn't contemplated the need for this outside of the REPL integration, so something tidier would be needed; e.g. adding cljx.install-cljx-load before all of your other :requires could be all that's necessary, if such a namespace were provided that had a top-level forcing the delay in question.

Oops, Github converted my hyperlink to a cross-reference, and I have no way to delete it.

Thanks, the code in the preceding comment solves this for me.

Unh, just ran into this also :/

Also, prepending the following doesn't seem to work, so I opted for the "profile without user.clj on source-path" -hack.

(require '[cljx.repl-middleware :as cljx])
(reset! @#'cljx/cljx-load-rules {:clj cljx.rules/clj-rules})
@@#'cljx/install-cljx-load

Another quick workaround is to comment the source path with user.clj in the project.clj before running cljx, then uncomment it for further compilation steps