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
|
|
|
|
namespace. So writing a Leiningen plugin is pretty straightforward; as
|
2011-07-18 04:47:55 +00:00
|
|
|
long as the file containing the namespace is available on the
|
|
|
|
classpath, Leiningen will be able to use it.
|
2010-06-05 21:00:37 +00:00
|
|
|
|
2010-11-10 06:20:22 +00:00
|
|
|
Plugins may be installed on a per-project or user-wide basis. To use a
|
|
|
|
plugin in a single project, add it to your project.clj
|
|
|
|
:dev-dependencies and run "lein deps". To install it for your user,
|
2011-07-18 04:47:55 +00:00
|
|
|
run "lein plugin install ARTIFACT-ID VERSION". In general user-level
|
|
|
|
plugins are preferred when the plugin is a matter of user convenience,
|
|
|
|
and :dev-dependencies are better for plugins without which the tests
|
|
|
|
or packaging will not function.
|
|
|
|
|
|
|
|
For example,
|
|
|
|
[swank-clojure](https://github.com/technomancy/swank-clojure) should
|
|
|
|
be installed with "lein plugin install" while
|
|
|
|
[lein-tar](https://github.com/technomancy/lein-tar) should be in
|
|
|
|
:dev-dependencies.
|
2010-11-07 23:50:25 +00:00
|
|
|
|
2010-06-05 21:00:37 +00:00
|
|
|
## Writing a Plugin
|
|
|
|
|
|
|
|
Start by generating a new project with "lein new myplugin", and add a
|
2010-11-07 23:50:25 +00:00
|
|
|
leiningen.myplugin namespace with a myplugin function. Add
|
|
|
|
:eval-in-leiningen true to your project.clj so Leiningen knows to
|
|
|
|
execute its code inside the Leiningen process rather than spinning up
|
2011-07-18 04:47:55 +00:00
|
|
|
a project subprocess.
|
2010-08-20 03:53:40 +00:00
|
|
|
|
|
|
|
Some tasks may only be run in the context of a project. For tasks like
|
|
|
|
this, name the first argument <tt>project</tt>. Leiningen will inspect
|
2011-07-18 04:47:55 +00:00
|
|
|
the argument list and pass in the current project if needed.
|
|
|
|
|
|
|
|
Some tasks can be run inside a project or outside, but would benefit
|
|
|
|
from having the project argument if they're run from a project. For
|
|
|
|
these, name the first argument something like <tt>project-or-foo</tt>,
|
|
|
|
and it will be passed the project argument when appropriate.
|
|
|
|
|
|
|
|
The project is a map which is based on the project.clj file, but it
|
|
|
|
also has :name, :group, :version, and :root keys added in. If you want
|
|
|
|
it to take parameters from the command-line invocation, you can make
|
|
|
|
the function take more arguments.
|
|
|
|
|
|
|
|
Note that Leiningen is an implied dependency of all plugins; you
|
|
|
|
should not explicitly list it in the project.clj file. You also don't
|
|
|
|
need to list Clojure, but you will be locked into using the same
|
|
|
|
version of Clojure that Leiningen is using. For Leiningen 1.x, a
|
|
|
|
dependency on Clojure Contrib is also implied, though this is gone in
|
|
|
|
2.0.
|
2010-06-22 02:21:13 +00:00
|
|
|
|
2011-04-12 20:02:18 +00:00
|
|
|
The "lein help" task will display the first line of the task
|
|
|
|
function's docstring as a summary. Then "lein help $TASK" will use
|
|
|
|
the task function's full docstring for detailed help. The function's
|
|
|
|
arglists will also be shown, so pick argument names that are clear and
|
2010-11-23 20:24:26 +00:00
|
|
|
descriptive. If you set :help-arglists in the function's metadata, it
|
2011-04-12 20:02:18 +00:00
|
|
|
will be used instead for those cases where alternate arities exist
|
|
|
|
that aren't intended to be exposed to the user.
|
2010-06-05 21:00:37 +00:00
|
|
|
|
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.
|
|
|
|
|
|
|
|
## Threads
|
|
|
|
|
|
|
|
Leiningen contains a workaround for a flaw in Clojure's agent thread
|
|
|
|
pools that may cause confusion. The JVM will
|
|
|
|
[refuse to exit](http://tech.puredanger.com/2010/06/08/clojure-agent-thread-pools/)
|
|
|
|
if there are active non-daemon threads. Clojure agents and futures
|
|
|
|
start up a non-daemon thread pool, so if you call any code that uses
|
|
|
|
agents or futures (even code that isn't inherently asynchronous like
|
|
|
|
<tt>clojure.java.shell/sh</tt>), the JVM won't exit until
|
|
|
|
<tt>(shutdown-agents)</tt> is called.
|
|
|
|
|
|
|
|
In order to work around this, Leiningen appends a call to
|
|
|
|
<tt>(shutdown-agents)</tt> to any code that runs in a project
|
|
|
|
subprocess. If it didn't, many plugins would simply never finish. But
|
|
|
|
it has a few unfortunate side-effects. You may expect the fact that
|
|
|
|
your plugin starts its own thread using <tt>future</tt> or
|
|
|
|
<tt>send</tt> to keep its process alive, but this is not possible with
|
|
|
|
Leiningen's workaround.
|
|
|
|
|
|
|
|
On the other hand, you may start up your own threads outside Clojure's
|
|
|
|
thread pools. These threads <i>will</i> keep the process alive on
|
|
|
|
their own, but they will not prevent Leiningen from running
|
|
|
|
<tt>(shutdown-agents)</tt>. This will cause code that's running in
|
|
|
|
your threads to lose access to agents and futures since
|
|
|
|
[shutdown-agents is irreversible](http://p.hagelb.org/shutdown-agents.jpg).
|
|
|
|
|
|
|
|
The solution is to block in your main thread if you need other threads
|
|
|
|
to keep alive. However, if your task is being run from Leiningen's
|
|
|
|
<tt>interactive</tt> task, the project subprocess will not exit and
|
|
|
|
<tt>shutdown-agents</tt> will not be triggered, so it's best to block
|
|
|
|
conditionally. Here's an example:
|
|
|
|
|
|
|
|
(when-not ~leiningen.core/*interactive?*
|
|
|
|
(shutdown-agents))
|
|
|
|
|
|
|
|
Note again that this only applies for plugins that need to run code in
|
|
|
|
the project's process, so the code above would go inside the
|
|
|
|
<tt>form</tt> argument to <tt>eval-in-project</tt>. If your plugin
|
|
|
|
only runs in Leiningen's process then you don't need to
|
|
|
|
worry. Hopefully this will be
|
|
|
|
[fixed in future versions of Clojure](http://dev.clojure.org/jira/browse/CLJ-124),
|
|
|
|
but the workaround will remain necessary for backwards-compatibility.
|
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
|
|
|
|
which wrap tasks and may alter their behaviour by using binding,
|
|
|
|
altering the return value, only running the function 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:
|
|
|
|
|
|
|
|
(use 'robert.hooke)
|
|
|
|
|
|
|
|
(defn skip-integration-hook [task & args]
|
2010-06-05 21:00:37 +00:00
|
|
|
(binding [clojure.test/test-var (test-var-skip :integration)]
|
2010-06-12 06:03:02 +00:00
|
|
|
(apply task args)))
|
2010-06-05 21:00:37 +00:00
|
|
|
|
2010-06-06 23:51:02 +00:00
|
|
|
(add-hook #'leiningen.test/test skip-integration-hook)
|
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
|
|
|
|
must set the :hooks key in project.clj to a seq of namespaces to load
|
|
|
|
that call add-hook. Note that in Leiningen 1.2, hooks get loaded and
|
|
|
|
used without being specified in project.clj; this is a bug. In 1.3 and
|
|
|
|
on they are opt-in only.
|
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
|
|
|
|
process, you may use <tt>leiningen.util.injected/add-hook</tt>, which
|
|
|
|
is an isolated copy of <tt>robert.hooke/add-hook</tt> injected into
|
|
|
|
the project in order to support features like test selectors.
|
|
|
|
|
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-07-18 04:47:55 +00:00
|
|
|
## Altering Leiningen's Classpath
|
|
|
|
|
|
|
|
Leiningen's classpath will include all plugins from :dev-dependencies
|
|
|
|
as well as user plugins. To further modify the classpath of Leiningen
|
|
|
|
itself, add a '.lein\_classpath' file a project's root. Its contents
|
|
|
|
will be prepended to Leiningen's classpath when Leiningen is invoked
|
|
|
|
upon that project.
|
|
|
|
|
|
|
|
## Lancet
|
2011-07-01 19:52:31 +00:00
|
|
|
|
2011-07-18 04:47:55 +00:00
|
|
|
If your plugins need to do a fair amount of filesystem-y things, you
|
|
|
|
may want to take a look at using Ant tasks to do them since the JDK
|
|
|
|
lacks a lot of simple functionality of this kind. Using the Ant API
|
|
|
|
directly is a pain, but it can be eased to a degree using
|
|
|
|
[Lancet](https://github.com/stuarthalloway/lancet). Lancet is the
|
|
|
|
Clojure adapter for Ant that is developed as the sample project in the
|
|
|
|
[Programming
|
|
|
|
Clojure](http://www.pragprog.com/titles/shcloj/programming-clojure)
|
|
|
|
book.
|
|
|
|
|
|
|
|
You can look over the [Ant API documentation's listing of
|
|
|
|
tasks](http://www.jajakarta.org/ant/ant-1.6.1/docs/en/manual/api/org/apache/tools/ant/taskdefs/package-summary.html)
|
|
|
|
to find an appropriate task. See the <tt>deps</tt> task for an example
|
|
|
|
of how to call a task from Clojure.
|
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.
|