3e1459a2b2
Fixes #2387.
589 lines
25 KiB
Markdown
589 lines
25 KiB
Markdown
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
|
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
|
|
|
- [Leiningen Plugins](#leiningen-plugins)
|
|
- [Writing a Plugin](#writing-a-plugin)
|
|
- [Local development](#local-development)
|
|
- [Task Arguments](#task-arguments)
|
|
- [Documentation and subtasks](#documentation-and-subtasks)
|
|
- [Code Evaluation](#code-evaluation)
|
|
- [Evaluating In Project Context](#evaluating-in-project-context)
|
|
- [Other Plugin Contents](#other-plugin-contents)
|
|
- [Profiles](#profiles)
|
|
- [Hooks](#hooks)
|
|
- [Project Middleware](#project-middleware)
|
|
- [Maven Wagons](#maven-wagons)
|
|
- [VCS Methods](#vcs-methods)
|
|
- [Requiring Plugins](#requiring-plugins)
|
|
- [Clojure Version](#clojure-version)
|
|
- [Upgrading Existing Plugins](#upgrading-existing-plugins)
|
|
- [Projects vs Standalone Execution](#projects-vs-standalone-execution)
|
|
- [Overriding Built-in Tasks](#overriding-built-in-tasks)
|
|
- [1.x Compatibility](#1x-compatibility)
|
|
- [Project-specific Tasks](#project-specific-tasks)
|
|
- [Have Fun](#have-fun)
|
|
|
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
|
|
[Japanese](ja/PLUGINS_ja.md)
|
|
|
|
# Leiningen Plugins
|
|
|
|
Leiningen tasks are simply functions named `$TASK` in a `leiningen.$TASK`
|
|
namespace. So writing a Leiningen plugin is just a matter of creating
|
|
a project that contains such a function, but much of this
|
|
documentation applies equally to the tasks that ship with Leiningen
|
|
itself.
|
|
|
|
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, users should place the
|
|
plugin declaration in the `:user` profile in `~/.lein/profiles.clj`
|
|
instead of directly in the `project.clj` file.
|
|
|
|
## Not Writing a Plugin (无为)
|
|
|
|
The first thing to do when writing a plugin is to try to accomplish
|
|
what you're doing without a plugin.
|
|
|
|
Early on in the days of Leiningen many plugins were written which did
|
|
nothing but provide a short command to run a specific function using
|
|
`eval-in-project`. Once Leiningen added the support for
|
|
partially-applied aliases these became largely redundant, because you
|
|
can add an alias to the `run` task:
|
|
|
|
:aliases {"mytest" ["run" "-m" "mylib.test/go"]}
|
|
|
|
Not only does this allow `lein mytest` to run the `mylib.test/go`
|
|
function inside the context of your project, it also passes additional
|
|
arguments (such as in the case of `lein run mytest :integration`) on
|
|
to the function specified. However, for some plugins this wasn't
|
|
enough as they needed access to values in the project map. For
|
|
instance, a test runner would need to know the value of `:test-paths`
|
|
to know which directory to scan for tests.
|
|
|
|
As of Leiningen 2.4.0 it's possible to get this data from an alias,
|
|
removing the need for a plugin.
|
|
|
|
:aliases {"mytest" ["run" "-m" "mylib.test/go" :project/test-paths]}
|
|
|
|
This will load the `:test-paths` value from the project map and pass a
|
|
string representation of it as the first argument to the function
|
|
specified in the alias, followed by any command-line arguments given
|
|
to the `mytest` alias. It's up to the function to call `read-string`
|
|
on that argument.
|
|
|
|
However, if you need to call other Leiningen functions or have no need
|
|
to run anything inside the context of the project's own process,
|
|
making a plugin might be the right choice if one doesn't [exist already](https://github.com/technomancy/leiningen/wiki/plugins),
|
|
|
|
## Writing a Plugin
|
|
|
|
Start by generating a new project with `lein new plugin
|
|
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 need not declare a dependency on
|
|
Clojure itself; in fact
|
|
[all of Leiningen's own dependencies](https://github.com/technomancy/leiningen/blob/stable/project.clj)
|
|
will be available.
|
|
|
|
See the `lein-pprint` directory
|
|
[in the Leiningen source](https://github.com/technomancy/leiningen/tree/stable/lein-pprint)
|
|
for a sample of a very simple plugin.
|
|
|
|
When emitting output, please use `leiningen.core.main/info`,
|
|
`leiningen.core.main/warn`, and `leiningen.core.main/debug` rather than
|
|
`println` since these will respect the user's output settings.
|
|
|
|
### Local development
|
|
|
|
When you're ready to test your plugin in a separate project you can include it via the following (example a plugin named sample-plugin):
|
|
|
|
```
|
|
lein install
|
|
Created ~/sample-plugin/target/sample-plugin-0.1.0-SNAPSHOT.jar
|
|
Wrote ~/sample-plugin/pom.xml
|
|
Installed jar and pom into local repo.
|
|
```
|
|
|
|
This will build a jar using the :version listed in the plugin's project.clj file (see above for example project.clj) and install it into your local m2 repository (~/.m2/repository)
|
|
|
|
After this step completes you can now list your plugin in your separate project with the version outputted from above. This example would look like this:
|
|
|
|
```
|
|
...
|
|
:plugins [[sample-plugin "0.1.0-SNAPSHOT"]]
|
|
...
|
|
```
|
|
|
|
During local development, having to re-run `lein install` in your
|
|
plugin project and then switch to a test project can be very
|
|
cumbersome. In order to avoid this annoyance, you can do the following:
|
|
|
|
1. If you haven't done it yet, run `lein install` in the plugin's project
|
|
directory.
|
|
2. Just to make sure, run `lein help <plugin-name>` in your test project
|
|
directory. A help message for your plugin should be displayed now. Or an
|
|
exception originating in your plugin.
|
|
3. Add the path to the `src` directory of your plugin to the file
|
|
`.lein-classpath` in your test project directory. Probably you'll have to
|
|
create that file.
|
|
4. If your plugin depends on another library that you are also working on, then
|
|
that needs to be added to `.lein-classpath` with the classpath separator,
|
|
either `:` for unix, or `;` for Windows. The same goes for your plugin's
|
|
other direct dependencies. Run `lein classpath` in order to get an idea how
|
|
the contents of `.lein-classpath` are supposed to look.
|
|
5. Remove the entry for your plugin from the test project's `project.clj`.
|
|
Otherwise it would override what you've added to `.lein-classpath`, because
|
|
Leiningen loads those things before it loads plugins.
|
|
|
|
### Task Arguments
|
|
|
|
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,
|
|
but it also has `:name`, `:group`, `:version`, and `:root` keys added
|
|
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
|
|
any project or combination of profiles.
|
|
|
|
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 unixy traditional `--dashed` syntax. Note that all arguments are
|
|
still passed in as strings; it's up to your function to call `read-string`
|
|
on the arguments if you want keywords, symbols, integers, etc. Keep
|
|
this in mind when calling other tasks as functions too.
|
|
|
|
Most tasks may only be run in the context of a project. If your
|
|
task can be run outside a project directory, add `^:no-project-needed`
|
|
as metadata to your task defn to indicate so. Your task must still
|
|
accept a project as its first argument, but it will be allowed to be
|
|
nil. Leiningen will still pass you the project as first argument if
|
|
lein is called from within a project. If called outside of a project,
|
|
lein will send in profile information from `$HOME/.lein/profiles.clj`
|
|
and similar sources as a map similar to a project map. Other tools using
|
|
the `leiningen-core` library (IDE integration, etc) may decide to just
|
|
pass in nil. To distinguish between a project and non-project, check for
|
|
the `:root` key. If it's set, then you are in a project, otherwise you
|
|
are not.
|
|
|
|
### Documentation and subtasks
|
|
|
|
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.
|
|
|
|
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.
|
|
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`.
|
|
|
|
Note that Leiningen doesn't have a mechanism for automatically invoking
|
|
subtasks. You'll have to do that yourself in the main task. A dumb
|
|
implementation of it all might look like this:
|
|
|
|
```clojure
|
|
(defn my-task
|
|
"Automatically write all the project's code."
|
|
{:subtasks [#'my-subtask-0 #'my-subtask-1]}
|
|
[project & [sub-name]]
|
|
(case sub-name
|
|
"my-subtask-0" (my-subtask-0 project args)
|
|
"my-subtask-1" (my-subtask-1 project args)
|
|
nil :not-implemented-yet
|
|
(leiningen.core.main/warn "Unknown task.")))
|
|
```
|
|
|
|
Leiningen will intercept calls to `lein $MYTASK help` by default and
|
|
turn them 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.
|
|
|
|
## 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
|
|
`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.
|
|
|
|
### Evaluating In Project Context
|
|
|
|
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
|
|
[Gilardi Scenario](https://technomancy.us/143).
|
|
|
|
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.
|
|
|
|
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:
|
|
|
|
```clj
|
|
(def swank-profile {:dependencies [['swank-clojure "1.4.3"]]})
|
|
|
|
(defn swank
|
|
"Launch swank server for Emacs to connect. Optionally takes PORT and HOST."
|
|
[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))))
|
|
```
|
|
|
|
The code in the `swank-clojure` dependency is needed inside the
|
|
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.
|
|
|
|
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.
|
|
|
|
## Other Plugin Contents
|
|
|
|
Plugins are primarily about providing tasks, but they can also contain
|
|
profiles, hooks, middleware, wagons (dependency transport methods),
|
|
and vcs methods.
|
|
|
|
### Profiles
|
|
|
|
If there is configuration that is likely to be used by many projects
|
|
using your plugin, yet for some reason you can't make that
|
|
configuration active by default, you can include profiles inside your
|
|
plugin.
|
|
|
|
Create a file called `resources/myplugin/profiles.clj` in your plugin that
|
|
contains a map:
|
|
|
|
```clj
|
|
{:default {:x "y and z"}
|
|
:extra {:other "settings"}}
|
|
```
|
|
|
|
Each value here is a profile that your users can merge into their
|
|
project. You can do this explicitly on a per-invocation basis using
|
|
`with-profile`:
|
|
|
|
$ lein with-profile plugin.myplugin/extra test
|
|
|
|
Users can also have profiles activated automatically by changing the `:default` profile:
|
|
|
|
```clj
|
|
:profiles {:default [:base :system :user :provided :dev :plugin.myplugin/default]
|
|
:other {...}}
|
|
```
|
|
|
|
Everything in the `:default` profile is active for all
|
|
non-`with-profile` task invocations except for those which produce
|
|
downstream artifacts, like `jar`, `uberjar`, and `pom`.
|
|
|
|
### Hooks
|
|
|
|
**Note**: Leiningen supports loading hooks from plugins; however this
|
|
mechanism is extremely error-prone and difficult to debug. It should
|
|
be considered deprecated as of 2.8.0 onward and will continue to work
|
|
until version 3.0 but is strongly advised against.
|
|
|
|
You can modify the behaviour of built-in Leiningen tasks to a degree
|
|
using hooks. Hook functionality is provided by the
|
|
[Robert Hooke](https://github.com/technomancy/robert-hooke) library,
|
|
which is included with Leiningen.
|
|
|
|
Inspired by clojure.test's fixtures functionality, hooks are functions
|
|
which wrap other functions (often tasks) and may alter their behaviour
|
|
by binding other vars, 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:
|
|
|
|
```clj
|
|
(ns lein-integration.plugin
|
|
(: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
|
|
(defn activate []
|
|
(robert.hooke/add-hook #'leiningen.test/form-for-testing-namespaces
|
|
#'add-test-var-println))
|
|
```
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
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
|
|
|
|
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
|
|
any kind of transformation on the project map. However, problems with
|
|
middleware can be difficult to debug due to their flexibility and
|
|
opaqueness. If you can do what you need using profiles inside your
|
|
plugins instead, that is a much more declarative, introspectable way
|
|
to do things which will save a lot of headache down the line.
|
|
|
|
The following middleware injects additional javac options into the project map,
|
|
but only if there are any java source paths in the project:
|
|
|
|
```clj
|
|
(ns leiningen.inject
|
|
(:require [leiningen.core.project :as p]))
|
|
|
|
(def javac-params-profile
|
|
{:javac-options ^:prepend ["-target" "1.6" "-source" "1.6"]})
|
|
|
|
(defn middleware [project]
|
|
(if (seq (:java-source-paths project))
|
|
(p/merge-profiles project [javac-params-profile])
|
|
project))
|
|
```
|
|
|
|
Projects use middleware by adding `:middleware` as a vector of var
|
|
names into their `project.clj`:
|
|
|
|
```clj
|
|
:middleware [leiningen.inject/middleware]
|
|
```
|
|
|
|
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.
|
|
|
|
If you need to include a profile in the project map, please add it as a plugin
|
|
profile and ask your users to add it to the `:base` profile as outlined in the
|
|
"Plugin" subsection of "Other Plugin Contents" in this document. This makes the
|
|
"injection" more explicit and easier to debug. The only times one should use
|
|
middleware to inject values into the project map is if the profiles has to be
|
|
programmatically computed, or if you have to modify the project map in a way
|
|
that is not possible with `merge-profiles`.
|
|
|
|
**Note**: Leiningen supports loading middleware implicitly when the
|
|
middleware is named `plugin-name.plugin/middleware`; however this
|
|
mechanism is even more difficult to debug than regular middleware. It
|
|
should be considered deprecated as of 2.8.0 onward and will continue
|
|
to work until version 3.0 but is strongly advised against.
|
|
|
|
### 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.
|
|
|
|
### VCS Methods
|
|
|
|
Leiningen ships with a `vcs` task which performs a handful of
|
|
release-related version control tasks via multimethods. Out of the box
|
|
it contains implementations for Git, but plugins can add support for
|
|
more systems by including a `leiningen.vcs.$SYSTEM` namespace. All
|
|
namespaces under the `leiningen.vcs.` prefix will be loaded when the
|
|
`vcs` task is invoked. These namespaces should simply define methods
|
|
for the `defmulti`s in `leiningen.vcs` that invoke the specific
|
|
version control system.
|
|
|
|
## 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]])
|
|
```
|
|
|
|
## Clojure Version
|
|
|
|
Leiningen 2.7.0 and on uses Clojure 1.8.0. If you need to use a
|
|
different version of Clojure from within a Leiningen plugin, you can
|
|
use `eval-in-project` with a dummy project argument:
|
|
|
|
```clj
|
|
(eval-in-project {:dependencies '[[org.clojure/clojure "1.4.0"]]}
|
|
'(println "hello from" *clojure-version*))
|
|
```
|
|
|
|
## 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"))
|
|
```
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
## Overriding Built-in Tasks
|
|
|
|
Normally if you create a plugin containing (say) a `leiningen.compile`
|
|
namespace, it won't be used when `lein compile` is run; the built-in
|
|
task will override it. If you'd like to shadow a built-in task, you
|
|
can either create an alias or put it in the `leiningen.plugin.compile`
|
|
namespace.
|
|
|
|
## Project-specific Tasks
|
|
|
|
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
|
|
it's much simpler to have your code run in a `-main` function inside your
|
|
project and invoke it with an alias like `lein garble`:
|
|
|
|
```clj
|
|
:aliases {"garble" ["run" "-m" "myproject.garble" "supergarble"]}
|
|
```
|
|
|
|
Note that aliases vectors result in partially applied task functions,
|
|
so with the above config, `lein garble seventeen` would be equivalent
|
|
to `lein run -m myproject.garble supergarble seventeen` (or
|
|
`(myproject.garble/-main "supergarble" "seventeen")` from the
|
|
repl). The arguments in the alias are concatenated to the arguments
|
|
provided when it's invoked.
|
|
|
|
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. You can even read values
|
|
from the project map with an alias:
|
|
|
|
```clj
|
|
:aliases {"garble" ["run" "-m" "myproject.garble" :project/version]}
|
|
```
|
|
|
|
This will splice the value of the project map's `:version` field into
|
|
the argument list so that the `-main` function running inside the
|
|
project code gets access to it.
|
|
|
|
The vast majority of these cases are already covered by
|
|
[existing plugins](https://github.com/technomancy/leiningen/wiki/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
|
|
`tasks/leiningen/` and add `tasks` to your `.lein-classpath`:
|
|
|
|
```
|
|
$ ls
|
|
README.md project.clj src tasks test
|
|
$ 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.
|
|
|
|
## Have Fun
|
|
|
|
Please add your plugin to [the list on the
|
|
wiki](https://github.com/technomancy/leiningen/wiki/plugins) once it's ready.
|
|
|
|
Hopefully the plugin mechanism is simple and flexible enough to let
|
|
you bend Leiningen to your will.
|