2010-06-05 21:00:37 +00:00
|
|
|
# Leiningen Plugins
|
|
|
|
|
2010-07-18 20:28:49 +00:00
|
|
|
Leiningen tasks are simply functions named $TASK in a leiningen.$TASK
|
2012-01-13 03:15:02 +00:00
|
|
|
namespace. So writing a Leiningen plugin is just a matter of creating
|
2013-01-03 06:32:07 +00:00
|
|
|
a project that contains such a function, but much of this
|
|
|
|
documentation applies equally to the tasks that ship with Leiningen
|
|
|
|
itself.
|
2012-01-13 03:15:02 +00:00
|
|
|
|
|
|
|
Using the plugin is a matter of declaring it in the `:plugins` entry
|
|
|
|
of the project map. If a plugin is a matter of user convenience rather
|
2012-08-06 19:41:25 +00:00
|
|
|
than a requirement for running the project, users should place the
|
2012-01-13 03:15:02 +00:00
|
|
|
plugin declaration in the `:user` profile in `~/.lein/profiles.clj`
|
|
|
|
instead of directly in the `project.clj` file.
|
2010-11-07 23:50:25 +00:00
|
|
|
|
2010-06-05 21:00:37 +00:00
|
|
|
## Writing a Plugin
|
|
|
|
|
2012-01-13 03:15:02 +00:00
|
|
|
Start by generating a new project with `lein new plugin
|
2013-09-21 13:08:35 +00:00
|
|
|
myplugin`, and edit the `myplugin` defn in the
|
2012-02-09 07:31:01 +00:00
|
|
|
`leiningen.myplugin` namespace. You'll notice the `project.clj` file
|
|
|
|
has `:eval-in-leiningen true`, which causes all tasks to operate
|
|
|
|
inside the leiningen process rather than starting a subprocess to
|
2012-06-27 23:06:48 +00:00
|
|
|
isolate the project's code. Plugins need not declare a dependency on
|
2012-02-09 07:31:01 +00:00
|
|
|
Clojure itself; in fact
|
2013-01-20 04:39:43 +00:00
|
|
|
[all of Leiningen's own dependencies](https://github.com/technomancy/leiningen/blob/stable/project.clj)
|
2012-06-10 04:08:50 +00:00
|
|
|
will be available. However, it doesn't hurt to be specific since
|
2012-06-27 23:06:48 +00:00
|
|
|
Leiningen's other dependencies may change in future versions.
|
2012-01-13 03:15:02 +00:00
|
|
|
|
2012-01-13 03:16:19 +00:00
|
|
|
See the `lein-pprint` directory
|
2013-01-20 04:39:43 +00:00
|
|
|
[in the Leiningen source](https://github.com/technomancy/leiningen/tree/stable/lein-pprint)
|
2012-01-13 03:16:19 +00:00
|
|
|
for a sample of a very simple plugin.
|
|
|
|
|
2012-06-10 04:08:50 +00:00
|
|
|
During plugin development, having to re-run `lein install` in your
|
|
|
|
plugin project and then switch to a test project can be very
|
2012-08-28 18:22:11 +00:00
|
|
|
cumbersome. Once you've installed the plugin once, you can avoid this
|
|
|
|
annoyance by creating a `.lein-classpath` file in your test project
|
|
|
|
containing the path to the `src` directory of your plugin.
|
2012-06-10 04:08:50 +00:00
|
|
|
|
2012-08-06 19:41:25 +00:00
|
|
|
### Task Arguments
|
2012-06-10 04:08:50 +00:00
|
|
|
|
2012-01-13 03:15:02 +00:00
|
|
|
The first argument to your task function should be the current
|
|
|
|
project. It will be a map which is based on the `project.clj` file,
|
2012-02-09 07:31:01 +00:00
|
|
|
but it also has `:name`, `:group`, `:version`, and `:root` keys added
|
2013-01-03 06:32:07 +00:00
|
|
|
in, among other things. Try using the `lein-pprint` plugin to see what
|
|
|
|
project maps look like; you can invoke the `pprint` task to examine
|
2014-01-20 18:49:21 +00:00
|
|
|
any project or combination of profiles.
|
2012-06-10 04:08:50 +00:00
|
|
|
|
2012-08-06 19:41:25 +00:00
|
|
|
If you want your task to take parameters from the command-line
|
|
|
|
invocation, you can make the function take more than one argument. In
|
|
|
|
order to underscore the fact that tasks are just Clojure functions,
|
|
|
|
arguments which act as flags are usually accepted as `:keywords`
|
|
|
|
rather than traditional `--dashed` syntax. Note that all arguments are
|
2014-01-20 18:49:21 +00:00
|
|
|
still passed in as strings; it's up to your function to call `read-string`
|
2012-08-06 19:41:25 +00:00
|
|
|
on the arguments if you want keywords, symbols, integers, etc. Keep
|
|
|
|
this in mind when calling other tasks as functions too.
|
2012-01-13 03:15:02 +00:00
|
|
|
|
|
|
|
Most tasks may only be run in the context of another project. If your
|
|
|
|
task can be run outside a project directory, add `^:no-project-needed`
|
2012-02-09 07:31:01 +00:00
|
|
|
metadata to your task defn to indicate so. Your task should still
|
|
|
|
accept a project as its first argument, but it will be allowed to be
|
|
|
|
nil if it's run outside a project directory. If you are inside a
|
2014-01-20 18:49:21 +00:00
|
|
|
project, Leiningen should `cd` to the root of that project before
|
2012-06-10 04:08:50 +00:00
|
|
|
launching the JVM, but some other tools using the `leiningen-core`
|
2013-08-01 05:02:16 +00:00
|
|
|
library (IDE integration, etc) may not behave the same way, so for
|
|
|
|
greatest portability check the `:root` key of the project map and work
|
|
|
|
from there.
|
2012-01-13 03:15:02 +00:00
|
|
|
|
2012-06-10 04:08:50 +00:00
|
|
|
### Documentation
|
2012-02-14 05:02:18 +00:00
|
|
|
|
2012-01-13 03:15:02 +00:00
|
|
|
The `lein help` task uses docstrings. A namespace-level docstring will
|
|
|
|
be used as the short summary if present; if not then it will take the
|
|
|
|
first line of your function's docstring. Try to keep the summary under
|
|
|
|
68 characters for formatting purposes. The full docstring can of
|
|
|
|
course be much longer but should still be wrapped at 80 columns. The
|
|
|
|
function's arglists will also be shown, so pick argument names that
|
|
|
|
are clear and descriptive. If you set `:help-arglists` in the
|
|
|
|
function's metadata, it will be used instead for those cases where
|
|
|
|
alternate arities exist that aren't intended to be exposed to the
|
|
|
|
user. Be sure to explain all these arguments in the docstring. Note
|
|
|
|
that all your arguments will be strings, so it's up to you to call
|
|
|
|
`read-string` on them if you want keywords, numbers, or symbols.
|
2010-06-05 21:00:37 +00:00
|
|
|
|
2012-08-06 19:41:25 +00:00
|
|
|
Often more complicated tasks get divided up into subtasks. Placing
|
|
|
|
`:subtasks` metadata on a task defn which contains a vector of subtask
|
|
|
|
vars will allow `lein help $TASK_CONTAINING_SUBTASKS` to list them.
|
2012-10-05 18:05:44 +00:00
|
|
|
This list of subtasks will show the first line of the docstring for each
|
|
|
|
subtask. The full help for a subtask can be viewed via
|
|
|
|
`lein help $TASK_CONTAINING_SUBTASKS $SUBTASK`.
|
2012-01-13 18:19:15 +00:00
|
|
|
|
2014-03-07 18:23:19 +00:00
|
|
|
Leiningen will intercept calls to `lein $MYTASK help` by default and
|
|
|
|
turn it into `lein help $MYTASK`. If your task provides its own help
|
|
|
|
subtask you can add `^:pass-through-help` metadata to your task defn
|
|
|
|
to opt-out of this behaviour.
|
|
|
|
|
2012-01-13 03:15:02 +00:00
|
|
|
## Code Evaluation
|
|
|
|
|
|
|
|
Plugin functions run inside Leiningen's process, so they have access
|
|
|
|
to all the existing Leiningen functions. The public API of Leiningen
|
|
|
|
should be considered all public functions inside the
|
2012-02-09 07:31:01 +00:00
|
|
|
`leiningen.core.*` namespaces not labeled with `^:internal` metadata
|
|
|
|
as well as each individual task functions. Other non-task functions in
|
|
|
|
task namespaces should be considered internal and may change inside
|
|
|
|
point releases.
|
2012-01-13 03:15:02 +00:00
|
|
|
|
2013-03-01 19:29:22 +00:00
|
|
|
### Evaluating In Project Context
|
|
|
|
|
2012-01-13 03:15:02 +00:00
|
|
|
Many tasks need to execute code inside the context of the project
|
|
|
|
itself. The `leiningen.core.eval/eval-in-project` function is used for
|
|
|
|
this purpose. It accepts a project argument as well as a form to
|
|
|
|
evaluate, and the final (optional) argument is another form called
|
|
|
|
`init` that is evaluated up-front before the main form. This may be
|
|
|
|
used to require a namespace earlier in order to avoid the
|
2012-08-22 22:35:21 +00:00
|
|
|
[Gilardi Scenario](http://technomancy.us/143).
|
2012-01-13 03:15:02 +00:00
|
|
|
|
2012-02-09 07:31:01 +00:00
|
|
|
Inside the `eval-in-project` call the project's own classpath will be
|
|
|
|
active and Leiningen's own internals and plugins will not be
|
2013-08-01 05:02:16 +00:00
|
|
|
available.
|
|
|
|
|
|
|
|
You can modify the project map before you pass it into `eval-in-project`.
|
|
|
|
However, it's recommended that you make your modifications by merging a
|
|
|
|
profile in so users can override your changes if necessary. Use
|
|
|
|
`leiningen.core.project/merge-profiles` to make your changes:
|
2012-02-09 07:31:01 +00:00
|
|
|
|
|
|
|
```clj
|
2013-08-01 05:02:16 +00:00
|
|
|
(def swank-profile {:dependencies [['swank-clojure "1.4.3"]]})
|
|
|
|
|
2012-02-09 07:31:01 +00:00
|
|
|
(defn swank
|
|
|
|
"Launch swank server for Emacs to connect. Optionally takes PORT and HOST."
|
2013-08-01 05:02:16 +00:00
|
|
|
[project port host & opts]
|
|
|
|
(let [profile (or (:swank (:profiles project)) swank-profile)
|
|
|
|
project (project/merge-profiles project [profile])]
|
|
|
|
(eval-in-project project
|
|
|
|
`(swank.core/-main ~@opts)
|
|
|
|
'(require 'swank.core))))
|
2012-02-09 07:31:01 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
The code in the `swank-clojure` dependency is needed inside the
|
2014-01-20 18:49:21 +00:00
|
|
|
project, so it's declared in its own profile map and merged
|
|
|
|
in. However, we defer to the `:swank` profile in the project map if
|
|
|
|
it's present so that the user can pick their own version of the
|
|
|
|
dependency if they like rather than relying on the hard-coded profile
|
|
|
|
in the plugin.
|
|
|
|
|
|
|
|
Note that the snippet above is not a good example of a plugin since it
|
|
|
|
simply wraps `eval-in-project` and `merge-profiles`. If that is all
|
|
|
|
you want, you can do it without implementing a plugin; just define an
|
|
|
|
alias that uses the `with-profiles` and `run` tasks to call the
|
|
|
|
function you need.
|
2012-01-16 04:43:12 +00:00
|
|
|
|
2012-08-06 19:41:25 +00:00
|
|
|
Before `eval-in-project` is invoked, Leiningen must "prep" a project,
|
|
|
|
usually by ensuring that all Java code and all necessary Clojure code
|
|
|
|
has been AOT compiled to bytecode. This is done by running all the
|
|
|
|
tasks in the `:prep-tasks` key of the project, which defaults to
|
|
|
|
`["javac" "compile"]`. If your plugin requires another kind of
|
|
|
|
prepping, (for instance, compiling protocol buffers) you can instruct
|
|
|
|
users to add another entry to `:prep-tasks`. Note that this task will
|
|
|
|
be invoked for **every** `eval-in-project`, so take care that it runs
|
|
|
|
quickly if nothing has changed since the last run.
|
2012-02-16 21:37:05 +00:00
|
|
|
|
2010-06-05 21:00:37 +00:00
|
|
|
## Hooks
|
|
|
|
|
|
|
|
You can modify the behaviour of built-in tasks to a degree using
|
2013-08-01 05:02:16 +00:00
|
|
|
hooks. Hook functionality is provided by the
|
|
|
|
[Robert Hooke](https://github.com/technomancy/robert-hooke) library,
|
|
|
|
which is included with Leiningen.
|
2010-06-12 06:03:02 +00:00
|
|
|
|
|
|
|
Inspired by clojure.test's fixtures functionality, hooks are functions
|
2012-01-13 03:15:02 +00:00
|
|
|
which wrap other functions (often tasks) and may alter their behaviour
|
2012-02-09 07:31:01 +00:00
|
|
|
by binding other vars, altering the return value, only running the function
|
2012-01-13 03:15:02 +00:00
|
|
|
conditionally, etc. The `add-hook` function takes a var of the task it's
|
|
|
|
meant to apply to and a function to perform the wrapping:
|
2010-06-12 06:03:02 +00:00
|
|
|
|
2011-11-11 20:16:52 +00:00
|
|
|
```clj
|
2012-08-22 22:35:21 +00:00
|
|
|
(ns lein-integration.plugin
|
2012-02-16 21:37:05 +00:00
|
|
|
(:require [robert.hooke]
|
|
|
|
[leiningen.test]))
|
|
|
|
|
|
|
|
(defn add-test-var-println [f & args]
|
|
|
|
`(binding [~'clojure.test/assert-expr
|
|
|
|
(fn [msg# form#]
|
|
|
|
(println "Asserting" form#)
|
|
|
|
((.getRawRoot #'clojure.test/assert-expr) msg# form#))]
|
|
|
|
~(apply f args)))
|
|
|
|
|
|
|
|
;; Place the body of the activate function at the top-level for
|
|
|
|
;; compatibility with Leiningen 1.x
|
2013-04-15 14:16:34 +00:00
|
|
|
(defn activate []
|
2012-02-16 21:37:05 +00:00
|
|
|
(robert.hooke/add-hook #'leiningen.test/form-for-testing-namespaces
|
2013-08-20 18:57:27 +00:00
|
|
|
#'add-test-var-println))
|
2011-11-11 20:16:52 +00:00
|
|
|
```
|
2010-06-05 21:00:37 +00:00
|
|
|
|
2013-08-20 18:57:27 +00:00
|
|
|
Hooks compose, so be aware that your hook may be running inside
|
|
|
|
another hook. See
|
|
|
|
[the documentation for Hooke](https://github.com/technomancy/robert-hooke/blob/master/README.md)
|
|
|
|
for more details. Note that calls to `add-hook` should use the var for
|
|
|
|
both the first and second argument so that hooks can be loaded
|
|
|
|
repeatedly without re-adding the hook. This is because in Clojure
|
|
|
|
bare functions cannot be compared for equality, but vars can.
|
2010-06-05 21:00:37 +00:00
|
|
|
|
2013-08-01 05:02:16 +00:00
|
|
|
If you want your hooks to be loaded automatically when other projects
|
|
|
|
include your plugin, activate them in a function called
|
|
|
|
`plugin-name.plugin/hooks`. So in the example above the plugin is
|
|
|
|
called `lein-integration`, and the function
|
|
|
|
`lein-integration.plugin/hooks` is automatically called to activate
|
|
|
|
hooks when the `lein-integration` plugin is loaded.
|
2012-08-22 22:35:21 +00:00
|
|
|
|
|
|
|
Hooks can also be loaded manually by setting the `:hooks` key in project.clj to
|
|
|
|
a seq of vars to call to activate your hooks. For backward compatibility, you
|
|
|
|
can also specify namespaces instead of vars in `:hooks`, and the `activate`
|
|
|
|
function in that namespace will be called. Note: automatic hooks are activated
|
|
|
|
before manually specified hooks.
|
|
|
|
|
|
|
|
## Project Middleware
|
|
|
|
|
2013-08-20 18:57:27 +00:00
|
|
|
Project middleware is just a function that is called on a project map
|
|
|
|
returning a new project map. Middleware gives a plugin the power to do
|
|
|
|
almost anything. For example, a plugin could use middleware to
|
|
|
|
reimplement Leiningen's profiles functionality.
|
2012-08-22 22:35:21 +00:00
|
|
|
|
2013-08-20 18:57:27 +00:00
|
|
|
The following middleware injects the contents of project map into your
|
|
|
|
project's resources folder so it can be read from the project code:
|
2012-08-22 22:35:21 +00:00
|
|
|
|
|
|
|
```clj
|
|
|
|
(ns lein-inject.plugin)
|
|
|
|
|
|
|
|
(defn middleware [project]
|
|
|
|
(update-in project [:injections] concat
|
|
|
|
`[(spit "resources/project.clj" ~(prn-str project))]))
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Like hooks, middleware will be applied automatically for plugins if you put it
|
|
|
|
in `plugin-name.plugin/middleware`. You can also load middleware manually by
|
|
|
|
setting the `:middleware` key in project.clj to a seq of vars to call to
|
|
|
|
transform your project map. Note that automatic middleware is applied before
|
|
|
|
manually specified middleware.
|
|
|
|
|
2013-08-20 18:57:27 +00:00
|
|
|
Also note that the currently active middleware depends on which
|
|
|
|
profiles are active. This means we need to reapply the middleware
|
|
|
|
functions to the project map whenever the active profiles change. We
|
|
|
|
accomplish this by storing the fresh project map and starting from
|
|
|
|
that whenever we call `merge-profiles`, `unmerge-profiles` or
|
|
|
|
`set-profiles`. It also means your middleware functions shouldn't have
|
|
|
|
any non-idempotent side-effects since they could be called repeatedly.
|
2012-08-22 22:35:21 +00:00
|
|
|
|
2014-04-07 13:58:25 +00:00
|
|
|
## Maven Wagons
|
|
|
|
|
|
|
|
[Pomegranate](https://github.com/cemerick/pomegranate) (the library
|
|
|
|
used by Leiningen to resolve dependencies) supports registering
|
|
|
|
"wagon" factories. Wagons are used to handle non-standard transport
|
|
|
|
protocols for repositories, and are looked up based on the protocol of
|
|
|
|
the repository url. If your plugin needs to register a wagon factory,
|
|
|
|
it can do so by providing a `leiningen/wagons.clj` file containing a
|
|
|
|
map of protocols to functions that return wagon instances for the
|
|
|
|
protocol. For example, the following `wagons.clj` will register a
|
|
|
|
wagon factory function for `dav:` urls:
|
|
|
|
|
|
|
|
```clj
|
|
|
|
{"dav" #(org.apache.maven.wagon.providers.webdav.WebDavWagon.)}
|
|
|
|
```
|
|
|
|
|
|
|
|
See [S3 wagon private](https://github.com/technomancy/s3-wagon-private) or
|
|
|
|
[lein-webdav](https://github.com/tobias/lein-webdav) for full examples of
|
|
|
|
plugins using this technique.
|
|
|
|
|
2012-08-23 20:29:37 +00:00
|
|
|
## Requiring Plugins
|
|
|
|
|
|
|
|
To use a plugin in your project, just add a `:plugins` key to your project.clj
|
|
|
|
with the same format as `:dependencies`. In addition to the options allowed by
|
|
|
|
`:dependencies`, `:plugins` also allows you to disable auto-loading of hooks or
|
|
|
|
middleware.
|
|
|
|
|
|
|
|
```clj
|
|
|
|
(defproject foo "0.1.0"
|
|
|
|
:plugins [[lein-pprint "1.1.1"]
|
|
|
|
[lein-foo "0.0.1" :hooks false]
|
|
|
|
[lein-bar "0.0.1" :middleware false]])
|
|
|
|
```
|
|
|
|
|
2011-11-11 20:16:52 +00:00
|
|
|
## Clojure Version
|
|
|
|
|
2014-01-20 18:49:21 +00:00
|
|
|
Leiningen 2.3.4 uses Clojure 1.5.1. If you need to use a different
|
2012-01-13 03:15:02 +00:00
|
|
|
version of Clojure from within a Leiningen plugin, you can use
|
|
|
|
`eval-in-project` with a dummy project argument:
|
2011-11-11 20:16:52 +00:00
|
|
|
|
|
|
|
```clj
|
2014-01-20 18:49:21 +00:00
|
|
|
(eval-in-project {:dependencies '[[org.clojure/clojure "1.4.0"]]}
|
2011-11-11 20:16:52 +00:00
|
|
|
'(println "hello from" *clojure-version*))
|
|
|
|
```
|
|
|
|
|
2012-02-09 07:31:01 +00:00
|
|
|
## Upgrading Existing Plugins
|
2012-01-13 03:15:02 +00:00
|
|
|
|
|
|
|
Earlier versions of Leiningen had a few differences in the way plugins
|
2012-02-09 07:31:01 +00:00
|
|
|
worked, but upgrading shouldn't be too difficult.
|
|
|
|
|
|
|
|
The biggest difference between 1.x and 2.x is that `:dev-dependencies`
|
|
|
|
have been done away with. There are no longer any dependencies that
|
|
|
|
exist both in Leiningen's process and the project's process; Leiningen
|
|
|
|
only sees `:plugins` and the project only sees `:dependencies`, though
|
|
|
|
both these maps can be affected by the currently-active profiles.
|
|
|
|
|
|
|
|
If your project doesn't need to use `eval-in-project` at all, it
|
|
|
|
should be relatively easy to port; it's just a matter of updating any
|
|
|
|
references to Leiningen functions which may have moved. All
|
|
|
|
`leiningen.utils.*` namespaces have gone away, and `leiningen.core`
|
|
|
|
has become `leiningen.core.main`. For a more thorough overview see the
|
|
|
|
[published documentation on leiningen-core](http://technomancy.github.com/leiningen/).
|
|
|
|
|
2012-02-09 16:54:46 +00:00
|
|
|
Plugins that do use `eval-in-project` should just be aware that the
|
2012-02-09 07:31:01 +00:00
|
|
|
plugin's own dependencies and source will not be available to the
|
|
|
|
project. If your plugin currently has code that needs to run in both
|
|
|
|
contexts it must be split into multiple projects, one for `:plugins`
|
|
|
|
and one for `:dependencies`. See the example of `lein-swank` above to
|
|
|
|
see how to inject `:dependencies` in `eval-in-project` calls.
|
|
|
|
|
2013-03-01 19:30:52 +00:00
|
|
|
## Projects vs Standalone Execution
|
|
|
|
|
|
|
|
Some Leiningen tasks can be executed from any directory (e.g. `lein repl`).
|
|
|
|
Some only make sense in the context of a project.
|
|
|
|
|
|
|
|
To check whether Leiningen is running in the context of a project
|
|
|
|
(that is, if a `project.clj` is present in the current directory),
|
|
|
|
check for the `:root` key in the project map:
|
|
|
|
|
|
|
|
``` clojure
|
|
|
|
(if (:root project)
|
|
|
|
(comment "Running in a project directory")
|
|
|
|
(comment "Running standalone"))
|
|
|
|
```
|
|
|
|
|
2012-02-09 16:54:46 +00:00
|
|
|
If your plugin may run outside the context of the project entirely,
|
|
|
|
you should still leave room in the arguments list for a project map;
|
|
|
|
just expect that it will be nil if there's no project present. Use
|
|
|
|
`^:no-project-needed` metadata to indicate this is acceptable.
|
|
|
|
|
2012-08-06 19:41:25 +00:00
|
|
|
In Leiningen 1.x, having a task function return a numeric value was a
|
|
|
|
way to signal the process's exit code. In Leiningen 2.x, tasks should
|
|
|
|
call the `leiningen.core.main/abort` function when a fatal error is
|
|
|
|
encountered. If the `leiningen.core.main/*exit-process?*` var is bound
|
|
|
|
to true, then this will trigger an exit, but in some contexts (like
|
|
|
|
`with-profiles`) it will simply trigger an exception and go on to the
|
|
|
|
next task.
|
|
|
|
|
2012-02-09 07:31:01 +00:00
|
|
|
## 1.x Compatibility
|
|
|
|
|
|
|
|
Once you've identified the changes necessary to achieve compatibility
|
|
|
|
with 2.x, you can decide whether you'd like to support 1.x and 2.x in
|
|
|
|
the same codebase. In some cases it may be easier to simply keep them
|
|
|
|
in separate branches, but sometimes it's better to support both.
|
|
|
|
Luckily the strategy of using `:plugins` and adding in `:dependencies`
|
2012-08-06 19:41:25 +00:00
|
|
|
just for calls to `eval-in-project` works fine in Leiningen 1.7.
|
2012-02-09 07:31:01 +00:00
|
|
|
|
|
|
|
If you use functions that moved in 2.x, you can try requiring and
|
|
|
|
resolving at runtime rather than compile time and falling back to the
|
|
|
|
1.x versions of the function if it's not found. Again the `lein-swank`
|
|
|
|
plugin provides an example of a compatibility shim:
|
|
|
|
|
|
|
|
```clj
|
|
|
|
(defn eval-in-project
|
|
|
|
"Support eval-in-project in both Leiningen 1.x and 2.x."
|
2012-02-23 00:56:11 +00:00
|
|
|
[project form init]
|
|
|
|
(let [[eip two?] (or (try (require 'leiningen.core.eval)
|
|
|
|
[(resolve 'leiningen.core.eval/eval-in-project)
|
|
|
|
true]
|
|
|
|
(catch java.io.FileNotFoundException _))
|
|
|
|
(try (require 'leiningen.compile)
|
|
|
|
[(resolve 'leiningen.compile/eval-in-project)]
|
|
|
|
(catch java.io.FileNotFoundException _)))]
|
|
|
|
(if two?
|
|
|
|
(eip project form init)
|
|
|
|
(eip project form nil nil init))))
|
2012-02-09 07:31:01 +00:00
|
|
|
```
|
2012-01-13 03:15:02 +00:00
|
|
|
|
2012-02-09 07:31:01 +00:00
|
|
|
Of course if the function has changed arities or has disappeared
|
|
|
|
entirely this may not be feasible, but it should suffice in most
|
|
|
|
cases.
|
2011-07-01 19:52:31 +00:00
|
|
|
|
2012-08-06 19:41:25 +00:00
|
|
|
Most widely-used functions which have changed in 2.x can be used from
|
|
|
|
the [leinjacker](https://github.com/sattvik/leinjacker) project, which
|
|
|
|
provides a compatibility shim supporting both 1.x and 2.x.
|
2012-04-24 17:14:24 +00:00
|
|
|
|
2012-02-23 00:56:11 +00:00
|
|
|
Another key change is that `:source-path`, `:resources-path`,
|
|
|
|
`:java-source-path`, and `:test-path` have changed to
|
2014-04-02 10:54:32 +00:00
|
|
|
`:source-paths`, `:resource-paths`, `:java-source-paths`, and
|
2012-02-23 00:56:11 +00:00
|
|
|
`:test-paths`, and they should be vectors now instead of single
|
|
|
|
strings. The old `:dev-resources` key is now just another entry to the
|
|
|
|
`:resource-paths` vector that's only present when the `:dev` profile
|
|
|
|
is active.
|
|
|
|
|
2012-02-09 16:54:46 +00:00
|
|
|
Allowing the task to run outside a project directory is tricky to do
|
|
|
|
in a backwards-compatible way since 1.x is overly-clever and actually
|
|
|
|
inspects your argument list to figure out if it should pass in a
|
|
|
|
project argument, while 2.x simply always passes it in and just allows
|
|
|
|
it to be nil if it's not present. You can try checking the first
|
|
|
|
argument to see if it's a project map, but if you have more than two
|
|
|
|
arities this can get very tricky; it may just be better to maintain
|
|
|
|
separate branches of your codebase in this situation.
|
|
|
|
|
2012-09-11 17:43:28 +00:00
|
|
|
### Project-specific Tasks
|
|
|
|
|
2012-10-23 15:22:13 +00:00
|
|
|
Occasionally, the need arises for a task to be included in a project's
|
|
|
|
codebase. However, this is much less common than people think. If you
|
|
|
|
simply have some code that needs to be invoked from the command-line
|
|
|
|
via `lein foo`, it's much simpler to have your code run inside your
|
|
|
|
project and alias `foo` to `run -m myproject.foo`:
|
|
|
|
|
2012-11-19 18:36:45 +00:00
|
|
|
```clj
|
2012-10-23 15:22:13 +00:00
|
|
|
:aliases {"foo" ["run" "-m" "myproject.foo"]}
|
|
|
|
```
|
|
|
|
|
|
|
|
You only need to write a Leiningen task if you need to operate outside
|
|
|
|
the context of your project, for instance if you need to adjust the
|
|
|
|
project map before calling `eval-in-project` or some other task where
|
|
|
|
you need direct access to Leiningen internals. The vast majority of
|
|
|
|
these cases are already covered by
|
|
|
|
[existing plugins](http://wiki.github.com/technomancy/leiningen/plugins),
|
|
|
|
but if you have a case that doesn't exist and for some reason can't
|
|
|
|
spin it off into its own separate plugin, you can enable this behavior
|
|
|
|
by placing the `foo.clj` file defining the new task in
|
2012-09-11 17:43:28 +00:00
|
|
|
`tasks/leiningen/` and add `tasks` to your `.lein-classpath`:
|
|
|
|
|
|
|
|
```
|
|
|
|
$ ls
|
2013-01-08 15:57:56 +00:00
|
|
|
README.md project.clj src tasks test
|
2012-09-11 17:43:28 +00:00
|
|
|
$ ls -R tasks
|
|
|
|
leiningen
|
|
|
|
|
|
|
|
tasks/leiningen:
|
|
|
|
foo.clj
|
|
|
|
$ echo -ne ":tasks" | cat >> .lein-classpath
|
|
|
|
$ lein foo
|
|
|
|
Hello, Foo!
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that in most cases it's better to spin off tasks into their own
|
|
|
|
plugin projects; using `.lein-classpath` is mainly appropriate for
|
|
|
|
experimentation or cases when there isn't enough time to create a
|
|
|
|
proper plugin.
|
|
|
|
|
2010-06-05 21:00:37 +00:00
|
|
|
## Have Fun
|
|
|
|
|
2012-06-27 23:06:48 +00:00
|
|
|
Please add your plugin to [the list on the
|
|
|
|
wiki](http://wiki.github.com/technomancy/leiningen/plugins) once it's ready.
|
2010-06-12 06:03:02 +00:00
|
|
|
|
2010-06-05 21:00:37 +00:00
|
|
|
Hopefully the plugin mechanism is simple and flexible enough to let
|
|
|
|
you bend Leiningen to your will.
|