-
-
Notifications
You must be signed in to change notification settings - Fork 180
Boot Troubleshooting
May happen if you try to access something external from within a pod. Only forms that can be printed with pr-str and read via read-string can pass betwen pods. That’s why you see that exception. You want to pass in the string path to the java.io.File object, not the object itself; i.e. pass ~(.getPath tgt)
instead of ~tgt
. The test it must pass is (= tgt (read-string (pr-str tgt)))
This is due to the way Clojure works; interfaces and classes are created dynamically at runtime, so two clojure runtimes can’t understand each other’s clojure things. Remember that pods are runtimes.
If you use e.g. core/by-ext to create sets of files and then you doseq over the set, you’ll get this exception if you used a string instead of a vector of strings for by-ext.
correct: (core/by-ext [".clj"] fs) incorrect: (core/by-ext ".clj" fs)
Scenario: $ boot aot -a
does not generate class files.
Resolution:
-
check your source paths: in
build.boot
you should have something like(set-env! :source-paths #{"src/clj"} ...)
-
make sure you have some .clj files in your source paths
-
add the
show
task to your pipeline to see what files are produced by your task. For example,boot aot show --fileset
will list the files in the input fileset fed to aot - that’s becauseboot aot
will just pass the input to the output fileset without compiling anything. To make it compile you must pass it a flag,-a
for all or-n foo.bar
to compile namespace foo.bar. Seeboot aot -h
. -
to see the results of aot, run
boot aot -a show --fileset
. Here theaot
task will compile all .clj files in your source paths, put them into its output fileset, and pass it to the next task,show
, which will display them in a nicely formatted tree. NOTE:show
will pass its input unchanged to the next task. If there is no next task, as in this example, nothing will be written to the target path. -
check your boot.properties files -
~/.boot/boot.properties
and./boot.properties
. If you seeBOOT_EMIT_TARGET=no
, your task will not write its output to disk - you will need to add thetarget
task to your pipeline. See BOOT_EMIT_TARGET. -
add the
target
task to your pipeline:boot aot -a show --fileset target -d "build/foo"
. The job of thetarget
task is to synchronize its input to the target path (directory). Here the contents of "build/foo" should match the listing produced byshow --fileset
. Of course, you can omitshow --fileset
from the pipeline, and pass the compiled fileset directly totarget
.
NOTE: The target
task performs a one-way, clean sync. It cleans the target path before it writes the fileset - anything in the target path will be deleted!
Clojure 1.8 ships with a socket server. The official docs instruct users to set a java system property that the Clojure runtime will pick up at startup. If you try to do that (via BOOT_JVM_OPTIONS
), the socket server will try to bind itself multiple times on the same port due to the fact that Boot launches multiple Clojure runtimes. Hilarity and chaos will ensue. Please consider starting the socket server programmatically, via the API.
For example:
BOOT_CLOJURE_VERSION=1.8.0 boot -i "(do (require 'clojure.core.server) ((resolve 'clojure.core.server/start-server) {:port 9999 :name :repl :accept 'clojure.core.server/repl}))" wait
Scenario: you want to run some code in (pod/with-eval-in @pod …) You added a dependency when you created the pod (e.g. (make-pod (update-in (get-env) [:dependencies] conj '[foo/bar "1.2.3"]))
; now you need to require the namespace, so you write (require 'foo.bar)
(or whatever). But you do some other stuff first, so this require
is within an if
or do
or let
or some other block. Now you try to use a var in the namespace, e.g. (foo.bar/baz)
. And you get a ClassNotFoundException for foo.bar
.
Resolution: This is a Clojure issue. Only top level require
gets invoked at compile time, which tells Clojure that foo.bar
is a namespace symbol. Knowing that, Clojure will treat (foo.bar/baz)
as referring to a namespaced symbol (var), rather than invocation of a static method in a Java class.
If your require
is not top level, it will fire at runtime, but by then it will be too late: Clojure saw (foo.bar/baz ..)
at compile time and interpreted it as a call to a static function baz
of a Java class foo.bar
. So require
executed at runtime will create a Namespace object for foo.bar and put it in the list available using (all-ns)
, and you can find your symbol in the interns map of the ns (try this: (doseq [[isym ivar] (ns-interns 'foo.bar)] (println "sym: " isym))
but when you try (foo.bar/baz ...)
you get ClassNotFoundException, because Clojure still thinks it is an attempt to call a static function in a Java class, which it cannot find.
If you need that kind of dynamism you need to use resolve
. Add something like (let [f (resolve 'foo.bar/baz)], and then call f
instead of foo.bar/baz
.
Scenario: Changes made to a file mounted with Docker / Vagrant between the guest and host os via NFS; filesystem events are not received correctly.
Resolution: Use rsync
in these environments, as NFS does not support inotify. You may also wish to run a repl server in the guest os and connect to it from the host.
Pods sometimes behave in surprising ways.
-
At least some namespaces in the enclosing environment are available in pods, but not their aliases. So if you get "No such namespace" for e.g.
(util/info "foo")
, try(boot.util/info "foo")
.
-
The verbosity of the task logging can be increased by using the
-v
flag. For even more verbosity-vv
can be used. Eg.In the REPL you use it like this (1-3, increasing verbosity):$ boot -v build
(reset! boot.util/*verbosity* 2) (boot (build))
-
Suppose you do
boot -v foobar
and you get a stack trace. You can then doboot -vb |cat -n
and see the matching line numbers. Actually you should doboot -vb foobar
to get the exact same generated code; just add -b to the thing that caused the error. -
boot show
is your friend. remember: it’s a task. that means you can insert it in anywhere in a pipeline in order to dump useful info to stdout. For example,boot show -f my-task show -f
will print the before and after filesets for my-task.
You can find other developers and users in the #hoplon
channel on freenode IRC or the boot slack channel.
If you have questions or need help, please visit the Discourse site.
- Environments
- Boot environment
- Java environment
- Tasks
- Built-ins
- Third-party
- Tasks Options
- Filesets
- Target Directory
- Pods
- Boot Exceptions
- Configuring Boot
- Updating Boot
- Setting Clojure version
- JVM Options
- S3 Repositories
- Scripts
- Task Writer's Guide
- Require inside Tasks
- Boot for Leiningen Users
- Boot in Leiningen Projects
- Repl reloading
- Repository Credentials and Deploying
- Snippets
- Troubleshooting
- FAQ
- API docs
- Core
- Pod
- Util