This repository has been archived by the owner. It is now read-only.

Custom Tasks

Raynes edited this page Jun 21, 2011 · 2 revisions

Custom tasks can be created using the deftask macro in either project.clj, tasks.clj or within your src directory. Any namespaces within src containing tasks must be added to the :tasks vector in project.clj and will be usable by other projects as plugins.

Like many build tools, Cake uses a dependency-based programming model. This means that if two tasks share a dependency, that dependency will only be run once. For more details, check out Martin Fowler's excellent article on Rake. Here is the example from that article using Cake syntax:

(deftask code-gen
  "This task generates code. It has no dependencies."
  (println "generating code...")

(deftask compile #{code-gen}
  "This task does the compilation. It depends on code-gen."
  (println "compiling...")

(deftask data-load #{code-gen}
  "This task loads the test data. It depends on code-gen."
  (println "loading test data...")

(deftask test #{compile data-load}
  "This task runs the tests. It depends on compile and data-load."
  (println "running tests...")

Dependencies for a task are denoted by a set after the task name. In this case, each dependency is just the name of a task that should be invoked before the task being defined.

File Tasks

The defile macro is used to define file generation tasks. Instead of a symbol, the task is named by a string that is the path to the file relative to the project

(defile "web.xml" #{"web.yaml"}
  "This task is only run if web.yaml is newer than web.xml"
  (println "generating web.xml from web.yaml...")

As shown by this example, defile dependencies can be the name of a file. Two things will happen in this case:

  1. the file task for web.yaml, if it exists, will be run before the web.xml task.
  2. the body of the web.xml task will only be run if web.yaml is newer than `web.xml.

You can also mix file and task dependencies for both macros. Within the dependency set, strings represent file tasks and symbols represent regular tasks.

(deftask deploy #{"web.xml" uberwar}
  "Always generate web.xml and uberwar before deploying")

Command-line Arguments

There is no way to pass parameters from one task to another, however, cake does parse all command-line arguments and make them available to all tasks as a var called *opts* which contains a map of keys to vectors of repeated values. Named args begin with --keyname and are mapped to :keyname. Unnamed arguments are mapped to :taskname. Repeated named values can be specified by repeating a key or by using commas in the value. Single and double dashes are both supported though a single dash followed by word characters without internal dashes or an equal sign is assumed to be single character argument flags and are split accordingly. A double dash marks the end of command line options that will be parsed by cake and put into *opts*; they will still be available in the raw version, *args*.

Here are some example cake commands followed be the corresponding values of *opts*:

cake help compile
{:help ["compile"]}

cake test :unit :functional foo.test.login-controller
{:test [":unit" ":functional" "foo.test.login-controller"]}

cake compile --compile-native=x86,debug --verbose -- -unparsed also-unparsed
{:compile-native ["x86", "debug"] :verbose [""]}

cake foo -vD -no-wrap -color=blue,green --style=baroque -color=red
{:style ["baroque"], :color ["blue" "green" "red"], :no-wrap [""], :D [""], :v [""]}

In the first two examples, you can see that unnamed arguments are placed under the task name in the opts map. This means you can pass "unnamed" arguments to a task that is a dependency of the one you are running by adding the task name before the arguments and separating them with commas.

You can also destructure *opts* directly in your task definitions:

(deftask test #{compile}
   "Run the tests specified on the command line."
   {test-names :test [verbose] :verbose}

(deftask foo #{bar}
   "This task takes a bunch of opts."
    {colors :color [no-wrap] :no-wrap [d] :D [v] :v
     {style 0 :or {style "modern"}} :style}

Extending and Redefining Tasks

Cake allows you to add actions, dependencies and even documentation to existing tasks. For example:

(deftask compile #{compile-native}
  "Native C code will be compiled before compiling Clojure and Java code.")

(deftask test
  (println "Running integration tests...")

Actions will be run in the order they are added, so if you extend Cake default tasks, your code will be run after the default code. All dependencies will be run before all actions, but there are no other guarantees about the order dependecies will be run.

Redefining a task

Sometimes you need to redefine a default task completely. In this case, you can use undeftask.

(undeftask release)
(deftask release
  "Release code to production servers."
  (println "Releasing to production...")

You can also use the :exclude option with the :tasks attribute in project.clj to prevent tasks from being defined in the first place.

Manually Calling a Task

If you have a conditional dependency or need to dynamically execute a task within another task for some other reason, you can use invoke.

(deftask primary
   (println "Executing primary task...")
   (when (:secondary opts)
      (invoke secondary))

(deftask secondary
   (println "Executing secondary task...")