-
-
Couldn't load subscription status.
- Fork 178
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 between 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.bootyou should have something like(set-env! :source-paths #{"src/clj"} …) -
make sure you have some .clj files in your source paths
-
add the
showtask to your pipeline to see what files are produced by your task. For example,boot aot show --filesetwill list the files in the input fileset fed to aot - that’s becauseboot aotwill just pass the input to the output fileset without compiling anything. To make it compile you must pass it a flag,-afor all or-n foo.barto compile namespace foo.bar. Seeboot aot -h. -
to see the results of aot, run
boot aot -a show --fileset. Here theaottask 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:showwill 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.propertiesand./boot.properties. If you seeBOOT_EMIT_TARGET=no, your task will not write its output to disk - you will need to add thetargettask to your pipeline. See BOOT_EMIT_TARGET. -
add the
targettask to your pipeline:boot aot -a show --fileset target -d "build/foo". The job of thetargettask 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 --filesetfrom 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}))" waitScenario: 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
-vflag. For even more verbosity-vvcan 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 foobarand you get a stack trace. You can then doboot -vb |cat -nand see the matching line numbers. Actually you should doboot -vb foobarto get the exact same generated code; just add -b to the thing that caused the error. -
boot showis 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 -fwill 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
