I recently implemented my first ".cljc" file that is supposed to bridge between clojure and clojurescript.
All went well, also figwheel is picking up the changes and nicely refreshes the new code, however in the clojure side the file is not hot-reloaded.
I'm using the usual
[ring.middleware.reload :refer [wrap-reload]]
in my development middleware.
In my project.clj, I have:
:source-paths ["src/clj" "src/cljc"]
Any ideas?
Make sure that source paths for both .clj and .cljc files are set at the top level in project.clj for the JVM compilation:
:source-paths ["src/clj" "src/cljc"]
And for the ClojureScript side, ensure that source paths are set anywhere that you have compilation directives for Figwheel e.g.:
; this might be your from your dev profile cljs config:
:cljsbuild
{:builds
{:app
{:source-paths ["src/cljs" "src/cljc" "env/dev/cljs"]
:compiler
{:main "my-project.app"
:asset-path "/js/out"
:output-to "target/cljsbuild/public/js/app.js"
:output-dir "target/cljsbuild/public/js/out"
:source-map true
:optimizations :none
:pretty-print true}}}}
Sounds like your Figwheel config is good, though.
Related
I have been dealing with this Clojure/Clojurescript project called clojurescript.csv.
On the project.clj file there is a special form of construction for :cljsbuild which I haven't seen yet.
:cljsbuild {:builds [{:id "whitespace"
:source-paths ["src" "test"]
:compiler {:output-to "target/js/whitespace.js"
:optimizations :whitespace
:pretty-print true}}
{:id "simple"
:source-paths ["src" "test"]
:compiler {:output-to "target/js/simple.js"
:optimizations :simple
:pretty-print true}}
{:id "advanced"
:source-paths ["src" "test"]
:compiler {:output-to "target/js/advanced.js"
:optimizations :advanced
:pretty-print false}}]
Usually, I see a brief declaration, such as:
:source-paths ["src"]
On this repository the approach is different. Although each "build path" uses the same source-path there are different ids and optimizations route.
1 - What is the point on having these different ids for builds? How can this be useful?
I do not see it.
2 - Also, I would like to extend this file for Continuous Deployment (publishing a Maven package on GitHub registry). Usually, below the source-paths, I add the following:
source-paths ["src"]
;; Change your environment variables (maybe editing .zshrc or .bashrc) to have:
;; export LEIN_USERNAME="pdelfino"
;; export LEIN_PASSWORD="your-personal-access-token-the-same-used-on-.npmrc"
;; LEIN_PASSWORD should use the same Token used by .npmrc
;; Also, do "LEIN_SNAPSHOTS_IN_RELEASE=true lein install" or edit your .zshrc:
;; export LEIN_SNAPSHOTS_IN_RELEASE=true
:repositories {"releases" {:url "https://maven.pkg.github.com/tallyfor/*"
:username :env/LEIN_USERNAME ;; change your env
:password :env/LEIN_PASSWORD}}
:pom-addition [:distribution-management [:repository [:id "github"]
[:name "GitHub Packages"]
[:url "https://maven.pkg.github.com/my-organization/repository-name"]]]
Should I add it 3 times? One for every id?
It feels very repetitive.
lein-cljsbuild allows you to specify multiple build configurations like this. When compiling you may provide the id of the build you want to build. So, instead of just lein cljsbuild once you do lein cljsbuild once advanced.
This is common so you can have an unoptimized development build and a :advanced optimized release build. Usually you'd have a few more differences in build configs, eg. at least the advanced build not including "test" source path. Since this is a library project, this however is fine. The authors likely wanted to test with different optimization levels easily.
:repositories and :pom-addition are top level or alias/profile definitions in project.clj. They do not go into the :cljsbuild config map.
I have a project where I want certain parts of the code to be able to run on a local environment, and other parts which depend on libraries that only run on a remote environment. E.g.
src/app/core.clj <- can run anywhere
src/app/sandbox/remote_only.clj <- depnds on libs that only function in remote environ
where src/app/core.clj is
(ns app.core
(:gen-class))
(defn -main [] (println "Hello, World!"))
and src/app/sandbox/remote_only.clj is
(ns app.sandbox.remote-only
(:require
[uncomplicate.commons.core :refer [with-release]]
[uncomplicate.neanderthal
[native :refer [dv dge]]
[core :refer [mv mv!]]]))
I want to be able to uberjar and run the code located in core.clj on my local machine without pulling in remote_only.clj which will cause the program to fail locally. According to the docs, Leiningen should be able to accomplish this using profiles and uberjar exclusions e.g.:
(defproject app "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.10.1"]]
:profiles {:uberjar {:main app.core
:init-ns app.core
:aot :all}
:remote {:init-ns app.sandbox.remote_only
:dependencies [[uncomplicate/neanderthal "0.43.1"]]}} ; these deps will fail locally
:uberjar-exclusions [#".*sandbox.*"]
:repl-options {:init-ns app.core})
compiling the uberjar here will result in:
❯ lein uberjar
Compiling app.core
Compiling app.sandbox.remote-only
Syntax error macroexpanding at (remote_only.clj:1:1).
Execution error (FileNotFoundException) at app.sandbox.remote-only/loading (remote_only.clj:1).
Could not locate uncomplicate/commons/core__init.class, uncomplicate/commons/core.clj or uncomplicate/commons/core.cljc on classpath.
So, explicitly its trying to compile the class that was specifically excluded.
I thought that this kind of problem could be avoided by removing the :all tag from :aot compilation. E.g.:
(defproject app "0.1.0-SNAPSHOT"
:dependencies [[org.clojure/clojure "1.10.1"]]
:profiles {:uberjar {:main app.core
:init-ns app.core
:aot [app.core]}
:remote {:init-ns app.sandbox.remote_only
:dependencies [[uncomplicate/neanderthal "0.43.1"]]}} ; these deps will fail locally
:uberjar-exclusions [#".*sandbox.*"]
:repl-options {:init-ns app.core})
which causes other problems:
❯ lein uberjar
Compiling app.core
Created /Users/warrenronsiek/Projects/app/target/app-0.1.0-SNAPSHOT.jar
Created /Users/warrenronsiek/Projects/app/target/app-0.1.0-SNAPSHOT-standalone.jar
~/Projects/app 5s 15:47:37
❯ java -jar ./target/app-0.1.0-SNAPSHOT.jar
Exception in thread "main" java.lang.NoClassDefFoundError: clojure/lang/Var
I've tried playing around with all kinds of :exclusions, :jar-exclusions and regexes. Variations of this question have been asked multiple times (1, 2) Nothing works.
How do I get Leiningen to compile uberjars and ignore the specified file(s)?
You are on the right track with changing :aot :all to :aot [app.core] but when you tried to run the JAR, you are running the library version instead of the whole application version:
java -jar ./target/app-0.1.0-SNAPSHOT.jar
That doesn't include Clojure or any dependencies. You want:
java -jar ./target/app-0.1.0-SNAPSHOT-standalone.jar
I haven't tried this particular setup with the "exclusions" features.
One workaround would be to create sub-projects for the "local" & "remote" parts of your app, which could then be used by a higher-level "total" project.
app
app-local
app-remote
where each of the 3 is a separate Lein project. app-local and app-remote could be developed in isolation. The top-level app project then uses has the 2 sub-projects as dependencies (and therefore only works in the remote/full environment).
The Lein feature checkouts allows you to develop in all 3 repos simultaneously, which is a huge help. It is much faster than the alternative of using lein install, which is slow, manual, repetitive, and error-prone.
P.S. Did you experiment with using lein jar instead of lein uberjar?
I'm working on two related web applications that both depend on a third local project for the code they have in common.
How can I get figwheel to rebuild and reload the code when the checkout dependency is edited?
At the moment, Figwheel doesn't automatically detect leiningen checkouts. You need to add the source paths of your checkout sources directly to your cljsbuild :source-paths. For example, if you had something like
:cljsbuild {:builds [{:id "dev"
:source-paths ["src" "dev"]
:figwheel {:on-jsload 'my.main/mount-gui}
:compiler {:output-to ...
:output-dir ...
:main 'my.main
...
then you would need to change it to
:cljsbuild {:builds [{:id "dev"
;; Add checkouts path here
:source-paths ["src" "dev" "checkouts/my-project/src"]
:figwheel {:on-jsload 'my.main/mount-gui}
:compiler {:output-to ...
:output-dir ...
:main 'my.main
...
Once figwheel knows about your checkout project source paths, it should automatically recompile after any changes, and reload the code, as it would for code in your main project.
I'm working on a pull request to fix this issue, which should make it work automatically in the future.
I want to create a war that I can deploy with tomcat. Using lein uberwar did the job just fine, however doing the same with boot doesn't seem to work. I can build a jar and run it, but the war fails with
Dec 09, 2015 12:15:31 AM org.apache.catalina.loader.WebappClassLoader validateJarFile INFO:
validateJarFile(/var/lib/tomcat7/sites/geeknow.guru/DEBUG##0.1.7/WEB-INF/lib/javax.servlet-api-3.1.0.jar)
- jar not loaded. See Servlet Spec 3.0, section 10.7.2. Offending class: javax/servlet/Servlet.class
I'm using the following build.boot
(set-env!
:source-paths #{"src/clj"}
:resource-paths #{"resources" "src/clj"}
:dependencies '[[org.clojure/clojure "1.7.0"]
[clj-time "0.9.0"]
[org.clojure/java.jdbc "0.3.7"]
[org.postgresql/postgresql "9.4-1202-jdbc41"]
[yesql "0.5.1"]
[migratus "0.8.6"]
[markdown-clj "0.9.67"]
[jarohen/nomad "0.7.2"]
[com.draines/postal "1.11.3"]
[compojure "1.4.0"]
[ring/ring-core "1.4.0"]
[ring/ring-devel "1.4.0"]
[ring/ring-defaults "0.1.5"]
[ring/ring-jetty-adapter "1.4.0"]
[ring-refresh "0.1.1"]
[ring-logger-timbre "0.7.5"]
[com.taoensso/timbre "4.1.4"]
[hiccup "1.0.5"]
[garden "1.3.0"]
[danlentz/clj-uuid "0.1.6"]
[speclj "3.3.1" :scope "test"]
[pandeiro/boot-http "0.7.1-SNAPSHOT"]])
(require '[pandeiro.boot-http :as http])
(require '[ring.middleware.reload :refer [wrap-reload]])
(require '[ring.adapter.jetty :as jetty])
(require '[ring.middleware.refresh :refer [wrap-refresh]])
(require '[blog.handler])
;;;;taken from boot-http.util
(defn resolve-sym [sym]
(require (symbol (namespace sym)) :reload)
(resolve sym))
(deftask ring-server
[]
(comp (jetty/run-jetty (wrap-refresh (wrap-reload (resolve-sym 'blog.handler/app))) {:port 3000}) (wait)))
(deftask uberwar
[]
(comp (aot) (pom) (web) (uber) (war)))
(deftask uberjar
[]
(comp (aot) (pom) (uber) (jar)))
(task-options!
pom {:project 'geeknow
:version "0.1.7"}
;uber {:as-jars true}
aot {:all true}
jar {:main 'blog.core
:manifest {"Description" "blog"}}
web {:serve 'blog.handler/app}
war {:main 'blog.core
:manifest {"Description" "blog"}}
repl {:init-ns 'blog.core})
Tomcat is complaining because one of the jars included in your war file contains a class that only the container environment can provide. The offending jar in the war is WEB-INF/lib/javax.servlet-api-3.1.0.jar.
By default, the uber task adds all dependencies, direct and transitive, to the war at WEB-INF/lib when --as-jars is set to true. --as-jars is the preferred way of bundling dependencies for deployment to servlet containers.
The problem appears to be that one of your direct dependencies is bringing in javax.servlet-api and uber is packaging it, causing the Tomcat problem.
First we need to figure out which dependency is bringing in javax.servlet-api. boot show contains many options for diagnosing dependency problems like this. You can learn about them all with boot show -h. The one we want now is boot show -d, which prints the dependency tree.
Here is the fragment of relevant output:
[ring/ring-jetty-adapter "1.4.0"]
├── [org.eclipse.jetty/jetty-server "9.2.10.v20150310"]
│ ├── [javax.servlet/javax.servlet-api "3.1.0"]
│ ├── [org.eclipse.jetty/jetty-http "9.2.10.v20150310"]
│ │ └── [org.eclipse.jetty/jetty-util "9.2.10.v20150310"]
│ └── [org.eclipse.jetty/jetty-io "9.2.10.v20150310"]
└── [ring/ring-servlet "1.4.0"]
From this output, we know that our dependency on ring/ring-jetty-adapter is what's causing javax.servlet/javax.servlet-api to be brought in.
Because this dependency is necessary for local development we don't want to omit it entirely. Instead, we can add the dependency with "test" scope:
[ring/ring-jetty-adapter "1.4.0" :scope "test"]
Scopes are a Maven concept for limiting the transitivity of dependencies in scenarios such as these. Boot uses Maven for its underlying dependency resolution machinery. uber is sensitive to Maven scopes, and can be configured to include or exclude various scopes depending on your needs. See boot uber -h for more information.
By default, the uber task won't package dependencies in "test" scope. By marking ring/ring-jetty-adapter this way, we've excluded it from our uber war.
The "test" scope is also useful for dependencies you might use in tests (of course!), or for deployment, or for other tasks during which you need a dependency but don't want to distribute it with your artifact, whether it's a library jar or an uberwar web app.
Disclaimer: I am very new to clojure.
I am suddenly running into a issue where loading my clojurescript app takes over 15 seconds just to load all of the library code. A second project, set up the same way, is not having these issues.
CLJS build:
:cljsbuild {:builds [{:id "dev"
:source-paths ["src/cljs"]
:compiler {:output-to "resources/public/app/js/app.js"
:output-dir "resources/public/app/js/out"
:optimizations :none
:source-map true}}]}
handler.clj
(GET "/" [] (resource-response "index.html" {:root "public/app"}))
(route/resources "/" {:root "public/app"})
(route/not-found "Not Found"))
My first thought is, that it somehow re-compiles everything every time I access the page, but the file timestamps of unchanged libraries didn't change.
Second thought was, that it is probably because of cache killing in my browser, but even after allowing cache through the developer tools, file loading time is still snailspeed.
Third thought was the compojure version difference between the 2 projects, but even after upgrading the 2nd project to latest, or downgrading 1st project to previous version, the issue still persists.
When monitoring, I also noticed the java process to jump to 350% CPU on page access.
I tried to revert all changes I made but can't figure out where the problem is. Being very new to clojure and clojurescript, I am out of ideas. I obviously can't wait 15 seconds with every page load just to see a message in the console.
/ EDIT:
Project.clj
(defproject picky "0.1.0-SNAPSHOT"
:description "project"
:url "http://example.com/FIXME"
:source-paths ["src/clj"]
:dependencies [[org.clojure/clojure "1.6.0"]
[org.clojure/tools.reader "0.8.2"]
[org.clojure/clojurescript "0.0-2371"]
[org.clojure/core.async "0.1.346.0-17112a-alpha"]
[ring/ring-core "1.3.1"]
[ring/ring-json "0.3.1"]
[compojure "1.2.1"]
[korma "0.4.0"]
[org.postgresql/postgresql "9.2-1002-jdbc4"]
[com.cemerick/friend "0.2.1"]
[lobos "1.0.0-beta3"]
[cljs-http "0.1.20"]
[secretary "1.2.1"]
[om "0.3.6"]
[com.facebook/react "0.8.0.1"]
[hiccup "1.0.5"]]
:plugins [[lein-cljsbuild "1.0.3"]
[lein-ring "0.8.13"]
[lein-pdo "0.1.1"]]
:aliases {"up" ["pdo" "cljsbuild" "auto" "dev," "ring" "server-headless"]}
:ring {:handler myapp.handler/app}
:profiles
{:dev {:dependencies [[javax.servlet/servlet-api "2.5"]
[ring-mock "0.1.5"]]}}
:cljsbuild {:builds [{:id "dev"
:source-paths ["src/cljs"]
:compiler {:output-to "resources/public/js/app.js"
:output-dir "resources/public/js/out"
:optimizations :none
:source-map true}}]}
Middlewares are wrap-json-body and wrap-json-response, although I tried already disabling both.
I found the culprit of this huge performance loss. After a ton of debugging I found out that the only difference between project 1 and project 2 is the resources folder.
Project 1 includes a entire frontend project. A lot of files through bower, scss, compiled css, compiled cljs and so on. Everything you need for good frontend development. In total 10506 files.
Project 2 was a test project including a few HTML, javascript and compiled cljs files. 142 files in total.
For testing, I moved some files out of the resources folder and ta-daa, file load is down to a few milliseconds. Move the files back into the resources folder, performance get tanked up.
I am going to file a bug report at the compojure project. Might be something they weren't aware of yet.