Commits

Meikel Brandmeyer committed dfad376

Transfer relevant code from base plugin

  • Participants
  • Parent commits d2a4a51

Comments (0)

Files changed (8)

File clojuresque-common-runtime/src/main/java/clojuresque/Driver.java

+/*-
+ * Copyright 2009-2013 © Meikel Brandmeyer.
+ * All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package clojuresque;
+
+import clojure.lang.RT;
+import clojure.lang.Var;
+
+public class Driver {
+    static final Var require = RT.var("clojure.core", "require");
+    static final Var apply   = RT.var("clojure.core", "apply");
+    static final Var symbol  = RT.var("clojure.core", "symbol");
+    static final Var seq     = RT.var("clojure.core", "seq");
+    static final Var next    = RT.var("clojure.core", "next");
+    static final Var sa      = RT.var("clojure.core", "shutdown-agents");
+
+    public static void main(String[] args) throws Exception {
+        int exitCode = 1;
+        final String command = args[0];
+
+        try {
+            require.invoke(symbol.invoke("clojuresque.util"));
+            final Var resolve  = RT.var("clojuresque.util", "resolve-required");
+            final Var driverFn = (Var)resolve.invoke(command);
+
+            if (driverFn == null)
+                throw new Exception(String.format("Unknown command: %s", command));
+
+            Boolean result = (Boolean)apply.invoke(
+                    driverFn.deref(),
+                    next.invoke(seq.invoke(args))
+            );
+            if (result)
+                exitCode = 0;
+        } finally {
+            sa.invoke();
+        }
+
+        System.exit(exitCode);
+    }
+}

File clojuresque-common-runtime/src/main/resources/clojuresque/cli.clj

+;-
+; Copyright 2009-2013 © Meikel Brandmeyer.
+; All rights reserved.
+; 
+; Permission is hereby granted, free of charge, to any person obtaining a copy
+; of this software and associated documentation files (the "Software"), to deal
+; in the Software without restriction, including without limitation the rights
+; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the Software is
+; furnished to do so, subject to the following conditions:
+; 
+; The above copyright notice and this permission notice shall be included in
+; all copies or substantial portions of the Software.
+; 
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+; THE SOFTWARE.
+
+(ns clojuresque.cli)
+
+(defn- str-cut
+  [s n]
+  (subs s 0 (- (count s) n)))
+
+(defn- print-usage
+  "Print usage information for the given option spec."
+  [description specs]
+  (println description)
+  (newline)
+  (println "Options:")
+  (doseq [spec (filter vector? specs)]
+    (let [loption (name (first spec))
+          spec    (rest spec)
+          soption (when (symbol? (first spec)) (name (first spec)))
+          spec    (if soption (rest spec) spec)
+          descr   (first spec)
+          default (first (rest spec))]
+      (print (format "  --%-10s " loption))
+      (when soption
+        (print (format "-%-3s " soption)))
+      (print descr)
+      (when default
+        (newline)
+        (print (format "                    The default is '%s'." default))))
+    (newline))
+  (flush))
+
+(defn with-command-line*
+  "Parse the command line arguments according to the given specifications.
+  A specification consists of a vector of an option name, an optional short
+  option name, a description and an optional default value. An option name
+  ending in ? designates a boolean flag. The last entry in the list of
+  specifications might be a symbol which is bound to the rest of the command
+  line arguments when -- or the first non-option argument is encountered.
+
+  -h, --help or -? stop the parsing and trigger the printing of the usage
+  message and thunk is not called.
+
+  thunk is called with a map of option-value pairs found on the command line."
+  [args description specs thunk]
+  (let [[options soptions]
+        (reduce (fn [[opts sopts] spec]
+                  (let [lopt  (name (first spec))
+                        sopt  (second spec)
+                        sopt  (if (symbol? sopt) (name sopt) nil)
+                        [lopt sopt type]
+                        (if (.endsWith lopt "?")
+                          [(str-cut lopt 1) sopt :flag]
+                          [lopt             sopt :option])]
+                    (vector (assoc opts lopt type)
+                            (assoc sopts sopt lopt))))
+                [{} {}]
+                (filter vector? specs))
+        rest-arg (when (symbol? (last specs)) (name (last specs)))]
+    (loop [args   (seq args)
+           argmap (hash-map)]
+      (let [arg (first args)]
+        (cond
+          (nil? args)
+          (if-not rest-arg
+            (thunk argmap)
+            (throw (Exception. "Missing command line arguments")))
+
+          (some #{arg} ["-h" "--help" "-?"])
+          (print-usage description specs)
+
+          (= arg "--")
+          (if rest-arg
+            (thunk (assoc argmap rest-arg (rest args)))
+            (throw (Exception.  "Unexpected command line arguments")))
+
+          (.startsWith arg "--")
+          (let [option (subs arg 2)]
+            (condp = (get options option)
+              :flag   (recur (next args) (assoc argmap option true))
+              :option (if-let [value (second args)]
+                        (recur (nthnext args 2) (assoc argmap option value))
+                        (throw
+                          (Exception.  (str "Missing value for option: " arg))))
+              nil     (throw (Exception. (str "Unknown option: " option)))))
+
+          (.startsWith arg "-")
+          (let [option (subs arg 1)]
+            (if-let [loption (get soptions option)]
+              (recur (cons (str "--" loption) (rest args)) argmap)
+              (throw (Exception. (str "Unknown option: " option)))))
+
+          :else
+          (if rest-arg
+            (thunk (assoc argmap rest-arg args))
+            (throw (Exception.  "Unexpected command line arguments"))))))))
+
+(defmacro with-command-line
+  "Parses the command line arguments given according to the specifications.
+  A specification consists of a vector of an option name, an optional short
+  option name, a description and an optional default value. An option name
+  ending in ? designates a boolean flag. The last entry in the list of
+  specifications might be a symbol which is bound to the rest of the command
+  line arguments when -- or the first non-option argument is encountered.
+
+  -h, --help or -? stop the parsing and trigger the printing of the usage
+  message and body is not executed.
+
+  The body is executed with the long option names bound to the value found
+  on the command line or the default value if the option was not given.
+  Flags default to nil, ie. logical false."
+  [args description specs & body]
+  (let [defaults (map (fn [spec]
+                        (cond
+                          (not (vector? spec)) [spec nil]
+
+                          (-> spec first name (.endsWith "?"))
+                          (vector (-> spec first name (str-cut 1) symbol) false)
+
+                          (-> spec second symbol?)
+                          (vector (first spec) (when (= (count spec) 4)
+                                                 (nth spec 3)))
+
+                          :else
+                          (vector (first spec) (when (= (count spec) 3)
+                                                         (nth spec 2)))))
+                      specs)]
+    `(with-command-line* ~args
+       ~description
+       (quote ~specs)
+       (fn [{:strs ~(vec (map first defaults))
+             :or   ~(into {} defaults)}]
+         ~@body))))
+
+(defmacro deftask
+  "Defines a new task which is callable from clojuresque via the runtime.
+  It adds command line option parsing around a function. So a „task“ is
+  also callable as a normal function with a sequence of options."
+  [name description options & body]
+  `(defn ~name
+     ~description
+     [& args#]
+     (with-command-line args#
+       ~description
+       ~options
+       ~@body)))

File clojuresque-common-runtime/src/main/resources/clojuresque/util.clj

+;-
+; Copyright 2009-2013 © Meikel Brandmeyer.
+; All rights reserved.
+; 
+; Permission is hereby granted, free of charge, to any person obtaining a copy
+; of this software and associated documentation files (the "Software"), to deal
+; in the Software without restriction, including without limitation the rights
+; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+; copies of the Software, and to permit persons to whom the Software is
+; furnished to do so, subject to the following conditions:
+; 
+; The above copyright notice and this permission notice shall be included in
+; all copies or substantial portions of the Software.
+; 
+; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+; THE SOFTWARE.
+
+(ns clojuresque.util)
+
+(defn namespace-of-file
+  [file]
+  (let [of-interest '#{ns clojure.core/ns}
+        eof         (Object.)
+        input       (clojure.lang.LineNumberingPushbackReader.
+                      (java.io.FileReader. file))
+        in-seq      (take-while #(not (identical? % eof))
+                                (repeatedly #(read input false eof)))
+        candidate   (first
+                      (drop-while
+                        #(or (not (instance? clojure.lang.ISeq %))
+                             (not (contains? of-interest (first %))))
+                        in-seq))]
+    (when candidate
+      (second candidate))))
+
+(defn namespaces
+  [files]
+  (distinct (keep namespace-of-file files)))
+
+(defn safe-require
+  [& nspaces]
+  (binding [*unchecked-math*     *unchecked-math*
+            *warn-on-reflection* *warn-on-reflection*]
+    (apply require nspaces)))
+
+(defn resolve-required
+  [fully-qualified-sym]
+  (let [slash  (.indexOf ^String fully-qualified-sym "/")
+        nspace (symbol (subs fully-qualified-sym 0 slash))
+        hfn    (symbol (subs fully-qualified-sym (inc slash)))]
+    (safe-require nspace)
+    (ns-resolve nspace hfn)))

File clojuresque-common/src/main/groovy/clojuresque/ClojureCommonPlugin.groovy

+/*-
+ * Copyright 2009-2013 © Meikel Brandmeyer.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+package clojuresque
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+
+public class ClojureCommonPlugin implements Plugin<Project> {
+    static final String CLOJURE_GROUP = "clojure development"
+
+    void apply(Project project) {
+        project.convention.plugins.clojure =
+            new ClojurePluginConvention(project)
+
+        project.configurations {
+            clojuresque {
+                transitive = false
+                visible = false
+                description = "Clojuresque internal configuration. Don't use!"
+            }
+            development {
+                transitive = true
+                visible = false
+                description = "Development only dependencies"
+            }
+        }
+    }
+}

File clojuresque-common/src/main/groovy/clojuresque/ClojurePluginConvention.groovy

+/*-
+ * Copyright 2009-2013 © Meikel Brandmeyer.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+package clojuresque
+
+import clojuresque.tasks.ClojureExecAction
+import kotka.gradle.utils.ConfigureUtil
+
+import org.gradle.api.Project
+import org.gradle.process.ExecResult
+
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+import groovy.lang.Closure
+
+class ClojurePluginConvention {
+    private static final Logger LOGGER = LoggerFactory.getLogger(ClojurePluginConvention)
+    private final Project project
+
+    public ClojurePluginConvention(Project project) {
+        this.project = project
+    }
+
+    public ExecResult clojureexec(Closure spec) {
+        ClojureExecAction action = ConfigureUtil.configure(
+            new ClojureExecAction(project.fileResolver,
+                project.configurations.clojuresque),
+            spec
+        )
+        return action.execute()
+    }
+}

File clojuresque-common/src/main/groovy/clojuresque/Util.groovy

+/*-
+ * Copyright 2012,2013 © Meikel Brandmeyer.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+package clojuresque
+
+import org.slf4j.Logger
+
+import java.util.Properties
+
+class Util {
+    static Properties properties(plugin) {
+        def props = new Properties()
+
+        Util.class.
+            getResourceAsStream("${plugin}.properties").
+            withReader("UTF-8") { props.load it }
+
+        return props
+    }
+
+    static deprecationWarning(Logger l, String o, String n) {
+        l.warn(String.format("'%s' is deprecated and will go away in a future version. Please use '%s' instead.", o, n))
+    }
+}

File clojuresque-common/src/main/java/clojuresque/tasks/ClojureExec.java

+/*
+ * Copyright 2009-2013 © Meikel Brandmeyer.
+ * All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package clojuresque.tasks;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.api.internal.project.ProjectInternal;
+import org.gradle.api.internal.ConventionTask;
+import org.gradle.api.tasks.TaskAction;
+import org.gradle.process.JavaExecSpec;
+import org.gradle.process.JavaForkOptions;
+import org.gradle.process.ProcessForkOptions;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+
+public class ClojureExec extends ConventionTask implements JavaExecSpec {
+    private ClojureExecAction clojureExecAction;
+
+    public ClojureExec() {
+        super();
+
+        FileCollection driverConf = getProject().getConfigurations().getByName("clojuresque");
+        FileResolver fileResolver = ((ProjectInternal)getProject()).getFileResolver();
+        clojureExecAction = new ClojureExecAction(fileResolver, driverConf);
+    }
+
+    @TaskAction
+    void exec() {
+        clojureExecAction.execute();
+    }
+
+    public List<String> getAllJvmArgs() {
+        return clojureExecAction.getAllJvmArgs();
+    }
+
+    public void setAllJvmArgs(Iterable<?> arguments) {
+        clojureExecAction.setAllJvmArgs(arguments);
+    }
+
+    public List<String> getJvmArgs() {
+        return clojureExecAction.getJvmArgs();
+    }
+
+    public void setJvmArgs(Iterable<?> arguments) {
+        clojureExecAction.setJvmArgs(arguments);
+    }
+
+    public ClojureExec jvmArgs(Iterable<?> arguments) {
+        clojureExecAction.jvmArgs(arguments);
+        return this;
+    }
+
+    public ClojureExec jvmArgs(Object... arguments) {
+        clojureExecAction.jvmArgs(arguments);
+        return this;
+    }
+
+    public Map<String, Object> getSystemProperties() {
+        return clojureExecAction.getSystemProperties();
+    }
+
+    public void setSystemProperties(Map<String, ?> properties) {
+        clojureExecAction.setSystemProperties(properties);
+    }
+
+    public ClojureExec systemProperties(Map<String, ?> properties) {
+        clojureExecAction.systemProperties(properties);
+        return this;
+    }
+
+    public ClojureExec systemProperty(String name, Object value) {
+        clojureExecAction.systemProperty(name, value);
+        return this;
+    }
+
+    public FileCollection getBootstrapClasspath() {
+        return clojureExecAction.getBootstrapClasspath();
+    }
+
+    public void setBootstrapClasspath(FileCollection classpath) {
+        clojureExecAction.setBootstrapClasspath(classpath);
+    }
+
+    public ClojureExec bootstrapClasspath(Object... classpath) {
+        clojureExecAction.bootstrapClasspath(classpath);
+        return this;
+    }
+
+    public String getMaxHeapSize() {
+        return clojureExecAction.getMaxHeapSize();
+    }
+
+    public void setMaxHeapSize(String heapSize) {
+        clojureExecAction.setMaxHeapSize(heapSize);
+    }
+
+    public String getMinHeapSize() {
+        return clojureExecAction.getMinHeapSize();
+    }
+
+    public void setMinHeapSize(String heapSize) {
+        clojureExecAction.setMinHeapSize(heapSize);
+    }
+
+    public boolean getEnableAssertions() {
+        return clojureExecAction.getEnableAssertions();
+    }
+
+    public void setEnableAssertions(boolean enabled) {
+        clojureExecAction.setEnableAssertions(enabled);
+    }
+
+    public boolean getDebug() {
+        return clojureExecAction.getDebug();
+    }
+
+    public void setDebug(boolean enabled) {
+        clojureExecAction.setDebug(enabled);
+    }
+
+    public String getMain() {
+        return clojureExecAction.getMain();
+    }
+
+    public ClojureExec setMain(String mainClassName) {
+        clojureExecAction.setMain(mainClassName);
+        return this;
+    }
+
+    public List<String> getArgs() {
+        return clojureExecAction.getArgs();
+    }
+
+    public ClojureExec setArgs(Iterable<?> applicationArgs) {
+        clojureExecAction.setArgs(applicationArgs);
+        return this;
+    }
+
+    public ClojureExec args(Object... args) {
+        clojureExecAction.args(args);
+        return this;
+    }
+
+    public JavaExecSpec args(Iterable<?> args) {
+        clojureExecAction.args(args);
+        return this;
+    }
+
+    public ClojureExec setClasspath(FileCollection classpath) {
+        clojureExecAction.setClasspath(classpath);
+        return this;
+    }
+
+    public ClojureExec classpath(Object... paths) {
+        clojureExecAction.classpath(paths);
+        return this;
+    }
+
+    public FileCollection getClasspath() {
+        return clojureExecAction.getClasspath();
+    }
+
+    public ClojureExec copyTo(JavaForkOptions options) {
+        clojureExecAction.copyTo(options);
+        return this;
+    }
+
+    public String getExecutable() {
+        return clojureExecAction.getExecutable();
+    }
+
+    public void setExecutable(Object executable) {
+        clojureExecAction.setExecutable(executable);
+    }
+
+    public ClojureExec executable(Object executable) {
+        clojureExecAction.executable(executable);
+        return this;
+    }
+
+    public File getWorkingDir() {
+        return clojureExecAction.getWorkingDir();
+    }
+
+    public void setWorkingDir(Object dir) {
+        clojureExecAction.setWorkingDir(dir);
+    }
+
+    public ClojureExec workingDir(Object dir) {
+        clojureExecAction.workingDir(dir);
+        return this;
+    }
+
+    public Map<String, Object> getEnvironment() {
+        return clojureExecAction.getEnvironment();
+    }
+
+    public void setEnvironment(Map<String, ?> environmentVariables) {
+        clojureExecAction.setEnvironment(environmentVariables);
+    }
+
+    public ClojureExec environment(String name, Object value) {
+        clojureExecAction.environment(name, value);
+        return this;
+    }
+
+    public ClojureExec environment(Map<String, ?> environmentVariables) {
+        clojureExecAction.environment(environmentVariables);
+        return this;
+    }
+
+    public ClojureExec copyTo(ProcessForkOptions target) {
+        clojureExecAction.copyTo(target);
+        return this;
+    }
+
+    public ClojureExec setStandardInput(InputStream inputStream) {
+        clojureExecAction.setStandardInput(inputStream);
+        return this;
+    }
+
+    public InputStream getStandardInput() {
+        return clojureExecAction.getStandardInput();
+    }
+
+    public ClojureExec setStandardOutput(OutputStream outputStream) {
+        clojureExecAction.setStandardOutput(outputStream);
+        return this;
+    }
+
+    public OutputStream getStandardOutput() {
+        return clojureExecAction.getStandardOutput();
+    }
+
+    public ClojureExec setErrorOutput(OutputStream outputStream) {
+        clojureExecAction.setErrorOutput(outputStream);
+        return this;
+    }
+
+    public OutputStream getErrorOutput() {
+        return clojureExecAction.getErrorOutput();
+    }
+
+    public JavaExecSpec setIgnoreExitValue(boolean ignoreExitValue) {
+        clojureExecAction.setIgnoreExitValue(ignoreExitValue);
+        return this;
+    }
+
+    public boolean isIgnoreExitValue() {
+        return clojureExecAction.isIgnoreExitValue();
+    }
+
+    public List<String> getCommandLine() {
+        return clojureExecAction.getCommandLine();
+    }
+
+    public void setDefaultCharacterEncoding(String defaultCharacterEncoding) {
+        clojureExecAction.setDefaultCharacterEncoding(defaultCharacterEncoding);
+    }
+
+    public String getDefaultCharacterEncoding() {
+        return clojureExecAction.getDefaultCharacterEncoding();
+    }
+}

File clojuresque-common/src/main/java/clojuresque/tasks/ClojureExecAction.java

+/*-
+ * Copyright 2009-2013 © Meikel Brandmeyer.
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+package clojuresque.tasks;
+
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.internal.file.FileResolver;
+import org.gradle.process.ExecResult;
+import org.gradle.process.internal.ExecHandle;
+import org.gradle.process.internal.JavaExecAction;
+import org.gradle.process.internal.JavaExecHandleBuilder;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ClojureExecAction extends JavaExecHandleBuilder implements JavaExecAction {
+    FileCollection driverClasspath;
+
+    public ClojureExecAction(FileResolver fileResolver, FileCollection driver) {
+        super(fileResolver);
+        this.driverClasspath = driver;
+    }
+
+    @Override
+    public List<String> getAllJvmArgs() {
+        List<String> allArgs = super.getAllJvmArgs();
+        String driver = driverClasspath.getAsPath();
+
+        int pos = allArgs.indexOf("-cp") + 1;
+        if (pos > 0) {
+            String oldClasspath = allArgs.remove(pos);
+            allArgs.add(pos, oldClasspath + File.pathSeparator + driver);
+        } else {
+            allArgs.add("-cp");
+            allArgs.add(driver);
+        }
+
+        return allArgs;
+    }
+
+    @Override
+    public List<String> getAllArguments() {
+        List<String> arguments = new ArrayList<String>();
+        arguments.addAll(getAllJvmArgs());
+        arguments.add("clojuresque.Driver");
+        arguments.add(getMain());
+        arguments.addAll(getArgs());
+        return arguments;
+    }
+
+    public ExecResult execute() {
+        ExecHandle execHandle = build();
+        ExecResult execResult = execHandle.start().waitForFinish();
+        if (!isIgnoreExitValue()) {
+            execResult.assertNormalExitValue();
+        }
+        return execResult;
+    }
+}