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
|
|
|
|
a project that contains such a function.
|
|
|
|
|
|
|
|
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
|
|
|
|
than a requirement for running the project, you should place the
|
|
|
|
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
|
2012-02-09 07:31:01 +00:00
|
|
|
lein-myplugin`, and edit the `myplugin` defn in the
|
|
|
|
`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
|
|
|
|
isolate the project's code. Plugins should not declare a dependency on
|
|
|
|
Clojure itself; in fact
|
2012-01-13 03:15:02 +00:00
|
|
|
[all of Leiningen's own dependencies](https://github.com/technomancy/leiningen/blob/master/project.clj)
|
|
|
|
should be considered implied dependencies of every plugin.
|
|
|
|
|
2012-01-13 03:16:19 +00:00
|
|
|
See the `lein-pprint` directory
|
2012-02-08 05:00:02 +00:00
|
|
|
[in the Leiningen source](https://github.com/technomancy/leiningen/tree/master/lein-pprint)
|
2012-01-13 03:16:19 +00:00
|
|
|
for a sample of a very simple plugin.
|
|
|
|
|
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
|
|
|
|
in, among other things. To see what project maps look like, try using
|
|
|
|
the `lein-pprint` plugin; then you can run `lein pprint` to examine
|
|
|
|
any project. If you want it to take parameters from the command-line
|
2012-01-13 03:15:02 +00:00
|
|
|
invocation, you can make the function take more arguments.
|
|
|
|
|
|
|
|
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
|
2012-01-13 03:15:02 +00:00
|
|
|
project, Leiningen should change to the root of that project before
|
|
|
|
launching the JVM, so `(System/getProperty "user.dir")` should be the
|
2012-02-09 07:31:01 +00:00
|
|
|
project root. The current directory of the JVM cannot be changed once
|
|
|
|
launched.
|
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-01-13 18:19:15 +00:00
|
|
|
TODO: document subtasks and subtask help
|
|
|
|
|
2010-07-18 20:28:49 +00:00
|
|
|
If your task returns an integer, it will be used as the exit code for
|
2011-07-18 04:47:55 +00:00
|
|
|
the process. If tasks are chained together, a nonzero integer return
|
|
|
|
value will halt the chain and exit immediately. Throwing an exception
|
|
|
|
will also halt execution, but returning an integer will avoid showing
|
|
|
|
an unsightly stack trace.
|
|
|
|
|
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
|
|
|
|
|
|
|
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-02-09 07:31:01 +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
|
|
|
|
available. However, it's easy to update the project map
|
|
|
|
that's passed to `eval-in-project` to add in the dependencies you
|
|
|
|
need. For example, this is done in the `lein-swank` plugin like so:
|
|
|
|
|
|
|
|
```clj
|
|
|
|
(defn swank
|
|
|
|
"Launch swank server for Emacs to connect. Optionally takes PORT and HOST."
|
|
|
|
([project port host & opts]
|
|
|
|
(eval-in-project (update-in project [:dependencies]
|
|
|
|
conj ['swank-clojure "1.4.0"])
|
|
|
|
(swank-form project port host opts))))
|
|
|
|
```
|
|
|
|
|
|
|
|
The code in the `swank-clojure` dependency is needed inside the
|
|
|
|
project, so it's `conj`ed into the `:dependencies`.
|
2012-01-16 04:43:12 +00:00
|
|
|
|
2012-01-13 03:15:02 +00:00
|
|
|
The return value of the `eval-in-project` call is an integer that
|
|
|
|
represents the exit code of the project's process. Zero indicates
|
2012-02-09 07:31:01 +00:00
|
|
|
success. Be sure to use this as the return value of your task function
|
|
|
|
if appropriate.
|
2010-06-19 03:56:36 +00:00
|
|
|
|
2010-06-05 21:00:37 +00:00
|
|
|
## Hooks
|
|
|
|
|
|
|
|
You can modify the behaviour of built-in tasks to a degree using
|
2010-07-18 20:28:49 +00:00
|
|
|
hooks. Hook functionality is provided by the [Robert
|
2010-12-03 02:16:16 +00:00
|
|
|
Hooke](https://github.com/technomancy/robert-hooke) library. This is an
|
2010-07-18 20:28:49 +00:00
|
|
|
implied dependency; as long as Leiningen 1.2 or higher is used it will
|
|
|
|
be available.
|
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-01-13 03:15:02 +00:00
|
|
|
(require 'robert.hooke)
|
2010-06-12 06:03:02 +00:00
|
|
|
|
2011-11-11 20:16:52 +00:00
|
|
|
(defn skip-integration-hook [task & args]
|
|
|
|
(binding [clojure.test/test-var (test-var-skip :integration)]
|
|
|
|
(apply task args)))
|
2010-06-05 21:00:37 +00:00
|
|
|
|
2012-02-09 07:31:01 +00:00
|
|
|
(defn activate []
|
|
|
|
(robert.hooke/add-hook #'leiningen.test/test
|
|
|
|
skip-integration-hook))
|
2011-11-11 20:16:52 +00:00
|
|
|
```
|
2010-06-05 21:00:37 +00:00
|
|
|
|
|
|
|
Hooks compose, so be aware that your hook may be running inside
|
2010-08-20 03:53:40 +00:00
|
|
|
another hook. To take advantage of your hooks functionality, projects
|
2012-01-13 03:15:02 +00:00
|
|
|
must set the `:hooks` key in project.clj to a seq of namespaces to load
|
2012-02-09 07:31:01 +00:00
|
|
|
that call `add-hook`. You may place calls to `add-hook` at the
|
|
|
|
top-level of the namespace, but if an `activate` defn is present it
|
|
|
|
will be called; this is the best place to put `add-hook` invocations.
|
2010-08-17 05:05:24 +00:00
|
|
|
|
2011-07-18 04:47:55 +00:00
|
|
|
If you need to use hooks from code that runs inside the project's
|
2012-01-13 06:48:44 +00:00
|
|
|
process, you may use `leiningen.core.injected/add-hook`, which is an
|
2012-01-13 03:15:02 +00:00
|
|
|
isolated copy of `robert.hooke/add-hook` injected into the project in
|
|
|
|
order to support features like test selectors.
|
2011-07-18 04:47:55 +00:00
|
|
|
|
2010-06-12 06:03:02 +00:00
|
|
|
See [the documentation for
|
2010-12-03 02:16:16 +00:00
|
|
|
Hooke](https://github.com/technomancy/robert-hooke/blob/master/README.md)
|
2010-06-12 06:03:02 +00:00
|
|
|
for more details.
|
2010-06-05 21:00:37 +00:00
|
|
|
|
2011-11-11 20:16:52 +00:00
|
|
|
## Clojure Version
|
|
|
|
|
2012-02-09 07:31:01 +00:00
|
|
|
Leiningen 2.0.0 uses Clojure 1.3.0. 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
|
2012-02-09 07:31:01 +00:00
|
|
|
(eval-in-project {:dependencies '[[org.clojure/clojure "1.4.0-beta1"]]}
|
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/).
|
|
|
|
|
|
|
|
Projects that do use `eval-in-project` should just be aware that the
|
|
|
|
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.
|
|
|
|
|
|
|
|
## 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`
|
|
|
|
just for calls to `eval-in-project` works fine in Leiningen 1.7. You
|
|
|
|
can even get support for profiles using `lein plugin install
|
|
|
|
lein-profiles 0.1.0`, though this support is experimental.
|
|
|
|
|
|
|
|
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."
|
|
|
|
[& args]
|
|
|
|
(let [eip (or (try (require 'leiningen.core.eval)
|
|
|
|
(resolve 'leiningen.core.eval/eval-in-project)
|
|
|
|
(catch java.io.FileNotFoundException _))
|
|
|
|
(try (require 'leiningen.compile)
|
|
|
|
(resolve 'leiningen.compile/eval-in-project)
|
|
|
|
(catch java.io.FileNotFoundException _)))]
|
|
|
|
(apply eip args)))
|
|
|
|
```
|
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
|
|
|
|
2010-06-05 21:00:37 +00:00
|
|
|
## Have Fun
|
|
|
|
|
2010-06-12 06:03:02 +00:00
|
|
|
Please add your plugins to [the list on the
|
|
|
|
wiki](http://wiki.github.com/technomancy/leiningen/plugins).
|
|
|
|
|
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.
|