lynaghk / cljx

Write a portable codebase targeting Clojure/ClojureScript

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Simplify configuration

jeluard opened this issue · comments

Most of my projects using cljx end up with a configuration similar to:

  :source-paths ["src" "target/generated-src"]
  :test-paths ["test" "target/generated-test"]
  :cljsbuild {:builds [{:source-paths ["src" "target/generated-src"]}]}
  :cljx {:builds [{:source-paths ["src"]
                   :output-path "target/generated-src"
                   :rules :clj}
                  {:source-paths ["src"]
                   :output-path "target/generated-src"
                   :rules :cljs}
                  {:source-paths ["test"]
                   :output-path "target/generated-test"
                   :rules :clj}
                  {:source-paths  ["test"]
                   :output-path "target/generated-test"
                   :rules :cljs}]}

Note that the cljx configuration has some implications on source-paths, test-paths and cljsbuild.

Most of it could be deduced and injected at run time (by a leiningen plugin). The configuration could be simplified down to:

:cljx {:builds [{:source-paths ["src" "test"]
                 :output-path "target/generated-%"
                 :rules [:clj :cljs]}]}

This is similar to this proposition for clojurescript.test.

The whole configuration could be omitted with some default:

  • source-paths is the concatenation of source-paths and test-paths
  • output-path has some default
  • rules defaults to [:clj :cljs]

Now this is probably too much (and assumes cljx files are in the same directory structures than clj files, which I usually do).

Yeah, I agree there's some configuration overload going on. I'm waffling on cemerick/clojurescript.test#20, and anything similar here; given my involvement of all of these projects (clojurescript.test, cljx, cljsbuild), I think I'm very susceptible to some myopia w.r.t. how they're used. I want them to be generally useful, and baking in "useful defaults" among the three (or more?) may lead to unintended mingling of concerns.

I'm leaning towards putting all cross-cutting configuration defaults/helpers into a separate "easy-cljs" middleware that encapsulates one way of using all of the projects together. I have something like this I use privately, and may just tweak that and put it out there.

FWIW, I always keep my Clojure, ClojureScript, and cljx files in separate directories under src. Maybe "big" choices like this that touch multiple plugins' operation can be shuffled off into the aforementioned middleware…

Thoughts welcome, per usual…

Right having a dedicated middleware makes complete sense and does not add coupling between those project.

Any specific reason why you keep your files in separate directories?

On Oct 19, 2013, at 5:37 PM, Julien Eluard notifications@github.com wrote:

Right having a dedicated middleware makes complete sense and does not add coupling between those project.

I agree too.
Any specific reason why you keep your files in separate directories?

I don't know about Chas, but I do as he does for separation of concerns purpose. I don't like to mix cljs (client) code with clj code (server code), or cljx portable code with the first two. I duplicate the same structure "src/clj, src/cljs and src/cljx" under the "test" directory too.
mimmo


Reply to this email directly or view it on GitHub.

I don't like to mix cljs (client) code with clj code (server code)

Ah right in this case it makes sense! I also have the scenario where I have a portable library with a few namespaces so different that I keep them as separate clj/cljs files. I was wondering if this could introduce some unintended effects (like incorrect jar packaging?).

On Oct 19, 2013, at 5:55 PM, Julien Eluard notifications@github.com wrote:

I was wondering if this could introduce some unintended effects (like incorrect jar packaging?).

for jar packaging I use the following practices:

  • :crossover-jar true for portable cli/cljs code (I call portable the code that has no differences between cli/cljs)
  • :jar true setting in the cljsbuild builds

mimmo

I end up with something like this:

:cljx {:builds [{:source-paths ["app/cljx"]
                   :output-path "app/generated/clj/samedhi/quizry"
                   :rules :clj}

                  {:source-paths ["app/cljx"]
                   :output-path "app/generated/cljs/samedhi/quizry"
                   :rules :cljs}

                  {:source-paths ["app/macro-cljx"]
                   :output-path "app/generated/clj/samedhi/quizry"
                   :rules
                   {:filetype "clj"
                    :features #{"clj"}
                    :transforms []}}

                  {:source-paths ["app/macro-cljx"]
                   :output-path "app/generated/cljs/samedhi/quizry"
                   :rules
                   {:filetype "clj"
                    :features #{"cljs"}
                    :transforms []}}]}

I do this because I have macros that need to reference different ns if they are in clj or cljs; however, since they are macros, they need to output as .clj files for both clj (of course) AND cljs. Just wanted to put my two cents in in case people were wondering what other peoples :cljx looked like. Of course, there may be a much better way to do this, and I would be happy to hear about it if anyone has any better ideas.

Chas taught me to use target dir for the generated stuff. This way when you issue the lein clean task it cleans the generated stuff even if you do not hook cljx to leon tasks.

Regarding the :feature trick for the macro I need to think about it. It seems to be smart.

mimmo

On Oct 24, 2013, at 12:01 AM, Stephen Cagle notifications@github.com wrote:

I end up with something like this:

:cljx {:builds [{:source-paths ["app/cljx"]
:output-path "app/generated/clj/samedhi/quizry"
:rules :clj}

              {:source-paths ["app/cljx"]
               :output-path "app/generated/cljs/samedhi/quizry"
               :rules :cljs}

              {:source-paths ["app/macro-cljx"]
               :output-path "app/generated/clj/samedhi/quizry"
               :rules
               {:filetype "clj"
                :features #{"clj"}
                :transforms []}}

              {:source-paths ["app/macro-cljx"]
               :output-path "app/generated/cljs/samedhi/quizry"
               :rules
               {:filetype "clj"
                :features #{"cljs"}
                :transforms []}}]}

I do this because I have macros that need to reference different ns if they are in clj or cljs; however, since they are macros, they need to output as .clj files for both clj (of course) AND cljs. Just wanted to put my two cents in in case people were wondering what other peoples :cljs looked like. Of course, there may be a much better way to do this, and I would be happy to hear about it if anyone has any better ideas.


Reply to this email directly or view it on GitHub.

Just in case you didn't know, you can also use something like

:clean-targets [:target-path "app/generated"]

to tell lein what directories you want removed on "lein clean"

Yeah, maybe it is a bit too "smart". My biggest issue with it is that I have two files with the same name, namespace, and that both end in clj. Somehow I end up always loading the #+clj version macro namespaces, so when I am compiling clojurescript code, I have to open up the #+cljs derived macro files and compile them manually before I build the .cljs files that depend on them. So, yeah, it works, but it is a bit of a mess.

At the moment the best way I found to not to be overwhelmed from all this very boring and fragile configuration has been to define lein-templates. As soon as I'll test them better I'll published. At the moment I defined a lein-template for any pure CLJS lib in which I injected:

  • clojurescript.test stuff
  • cljx stuff
  • cljsbuild stuff (crossovers included, because I always use crossover for portable CLJ/CLJS lib and cljx for ported lib)
  • piggieback stuff (which means brepl connection and ring server too.

I also kept separated all the development and testing stuff in the :dev profile of the profiles.clj local to the project.
The general template call will be something like: lein new cljs-start newlib and everything will be configured by the plugin itself. But then you have to make your hands dirty while adding stuff to the lib. It's only a workaround, but it saved me a lot of time and headache in shooting even typos....

there should be much better way to simplify all this stuff.

Trimming the scope of 0.5.0 so as to cut a release addressing pressing issues. Will circle back later.