Skip to content


Subversion checkout URL

You can clone with
Download ZIP


Added hook-eval-in-project, get-classpath, and apply-task #4

merged 6 commits into from

3 participants


hook-eval-in-project is a powerful tool to allow for functionality like lein2's :injections to be made available in lein1 w/ a bit more work. Also, it allows for plugins to invoke the JVM using entirely different code, for instance, to integrate w/ existing proprietary JVM launchers. One example of this is MATLAB, which allows for user code to be put on the classpath.

apply-task make it easy for one task to chain/delegate to another as a subtask.

get-classpath is useful when changing the JVM invocation, so that the new JVM can receive the classpath from all the right places.


Would it be possible to get feedback or a merge on this? I have a project that depends on these changes that I'd like to distribute, so I may otherwise have to fork and release so that I can release my project.

Thank you!


@dgrnbrg: get-classpath and apply-task look good to me. I'm curious about hook-eval-in-project though - I don't quite understand what it would be used for. Can you provide a better example of its usage?

@tobias tobias referenced this pull request

Add abort defn. #5


One example is here:

I needed to have the opportunity to inject some code into every eval-in-project, since the :injections key in the project map is only supported in lein2.

The other place I use this is to support lein2's :eval-in with lein1. I have a leiningen plugin that I use in a proprietary codebase that requires the JVM to invoked through a special wrapper script (which is used to configure LD_LIBRARY_PATH and jvm properties), and the wrapper script doesn't match regular java's argument passing either. So, I need to run a bunch of code to munge the project map into a command line with this java-invocation tool.

Does that clarify my usage scenarios?


Yes, thanks - that makes sense to me. I'm fine with merging this if @sattvik is. Assuming he gives it a thumbs up, would you be willing to add these functions to the README and add a test for hook-eval-in-project?


I've added the tests and updated the README after a long hiatus. Can this be merged now?



I'll take a look at it this weekend. So long as I don't run into any issues, my goal is to create a new release by Monday.


Since I've pushed changes that may affect these commits, I'll handle merging them in.

@tobias tobias merged commit 31e95a2 into sattvik:master

@dgrnbrg - I just pushed v0.3.0 to clojars:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 29, 2012
  1. @dgrnbrg

    Added hook-eval-in-project

    dgrnbrg authored
  2. @dgrnbrg
  3. @dgrnbrg

    Added agnostic get-classpath

    dgrnbrg authored
Commits on Sep 26, 2012
  1. @dgrnbrg


    dgrnbrg authored
  2. @dgrnbrg
  3. @dgrnbrg

    Updated readme

    dgrnbrg authored
This page is out of date. Refresh to see the latest.
@@ -3,7 +3,10 @@
leinjacker is a library of utilities for Leiningen plug-in developers. Current features include:
1. `leinjacker.eval` gives you an easy way for your project to call `eval-in-project` or `sh`
- and have it work independent of the version of Leiningen the user is running.
+ and have it work independent of the version of Leiningen the user is running. Additionally,
+ there is a function `hook-eval-in-project`. This function can be used provide custom behavior
+ (like middleware) for `eval-in-project`, such as for code instrumentation or alternative JVM
+ launching scripts.
2. `leinjacker.deps` adds some handy functions for querying and manipulating the dependencies
of a project.
@@ -21,6 +24,8 @@ leinjacker is a library of utilities for Leiningen plug-in developers. Current
abstracts away the location and name of Leiningen's project read function,
since it was renamed between generations.
* `abort` - 1.x- and 2.x-compatible way to signal task failure.
+ * `apply-task` - 1.x- and 2.x-compatible way to apply arguments to a task.
+ * `get-classpath` - 1.x- and 2.x-compatible way to get the classpath from a project map.
## Usage
@@ -46,6 +51,7 @@ If you ever find yourself writing the same generic code in more than one of your
* Daniel Solano Gómez
* Tobias Crawley
* Phil Hagelberg
+* David Greenberg
## License
37 src/leinjacker/eval.clj
@@ -1,8 +1,29 @@
(ns leinjacker.eval
- "Provides an eval-in-project function that should work for both Leiningen 1
- and Leiningen 2."
+ "Provides an eval-in-project function and hook that should work for both Leiningen 1
+ and Leiningen 2. Also provides apply-task, to chain to other tasks."
{:author "Daniel Solano Gómez"}
- (:require [leinjacker.utils :as utils]))
+ (:require [leinjacker.utils :as utils])
+ (:require [robert.hooke]))
+(defn hook-eval-in-project
+ "Takes a hook function `f` of 4 arguments, and hooks eval-in-project
+ with `robert.hooke`'s semantics.
+ This is used to write leiningen plugins that support injections
+ in a way compatible with Leiningen 1.x, or to support alternative/proprietary
+ scripts/programs to launch the JVM.
+ The 4 arguments are `[original-eip, project, form, init]`."
+ [f]
+ (let [gen (utils/lein-generation)]
+ (let [eip1 (fn [eip project form _ _ init] (f eip project form init))
+ eip2 (fn [eip project form init] (f eip project form init))]
+ (condp = gen
+ 1 (robert.hooke/add-hook
+ (utils/try-resolve 'leiningen.compile/eval-in-project) eip1)
+ 2 (robert.hooke/add-hook
+ (utils/try-resolve 'leiningen.core.eval/eval-in-project) eip2)
+ (throw (IllegalStateException. "Unknown Leiningen generation."))))))
(defn eval-in-project
"Support eval-in-project for both Leiningen 1.x and 2.x. This code is
@@ -23,3 +44,13 @@
(apply (utils/try-resolve-any
'leiningen.core.eval/sh) args))
+(defn apply-task
+ "Allow for invoking other Leiningen tasks. Takes the args as a seq."
+ [subtask project args]
+ (if-let [apply-task (utils/try-resolve 'leiningen.core.main/apply-task)]
+ (apply-task subtask project args)
+ ((utils/try-resolve 'leiningen.core/apply-task)
+ subtask project args
+ ;lein1 has a 4 argument form, which expects a not-found fn.
+ (utils/try-resolve 'leiningen.core/task-not-found))))
8 src/leinjacker/utils.clj
@@ -57,9 +57,15 @@
+(defn get-classpath
+ "Gets the classpath for a given project."
+ [project]
+ (try-resolve-any 'leiningen.core.classpath/get-classpath
+ 'leiningen.classpath/get-classpath) project)
(defn abort
"Signal a fatal error and print msg to stderr."
[& msg]
(let [abort (try-resolve-any 'leiningen.core/abort ; lein1
'leiningen.core.main/abort)] ; lein2
- (apply abort msg)))
+ (apply abort msg)))
16 test-project/test/leinjacker/eval_test.clj
@@ -22,4 +22,18 @@
(fact "sh should work."
(sh "./bin/sh-test" ".test-output" "success")
- (slurp ".test-output") => "success"))
+ (slurp ".test-output") => "success")
+ ;This test should be last since it hooks eip and doesn't clean up
+ (fact "we should be able to hook eval-in-project"
+ (let [my-form '(spit ".test-output" "success")
+ ran-form (atom nil)]
+ (hook-eval-in-project
+ (fn [original-eip project form init]
+ (reset! ran-form form)
+ (original-eip project form init)))
+ (dep-eip/eval-in-project
+ (utils/read-lein-project)
+ my-form)
+ @ran-form => my-form
+ (slurp ".test-output") => "success")))
Something went wrong with that request. Please try again.