summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2008-11-26 23:57:25 +0000
committerRich Hickey <richhickey@gmail.com>2008-11-26 23:57:25 +0000
commit2adf03dc22be0cf9caf93b4999528b3a06eb9002 (patch)
treeca05486e75cb376c1c946d062740119063a8b92b
parent4d944fea131f35d4bb88822127274d25bfcef100 (diff)
added clojure.main, patch from Stephen C. Gilardi
-rw-r--r--build.xml98
-rw-r--r--src/clj/clojure/main.clj231
-rw-r--r--src/clj/precompile.clj11
-rw-r--r--src/jvm/clojure/lang/Compile.java71
4 files changed, 353 insertions, 58 deletions
diff --git a/build.xml b/build.xml
index a1d3490f..64744fba 100644
--- a/build.xml
+++ b/build.xml
@@ -1,51 +1,55 @@
<project name="clojure" default="jar">
- <description>
- Build with "ant jar" and then start the REPL
- via "java -cp clojure.jar clojure.lang.Repl src/boot.clj".
- </description>
-
- <property name="src" location="src"/>
- <property name="jsrc" location="${src}/jvm"/>
- <property name="cljsrc" location="${src}/clj"/>
- <property name="build" location="classes"/>
- <property name="clojure_jar" location="clojure.jar"/>
- <property name="bootclj" location="${cljsrc}/clojure/core.clj"/>
- <property name="precompile" location="${cljsrc}/precompile.clj"/>
-
- <target name="init">
- <tstamp/>
- <mkdir dir="${build}"/>
- </target>
-
- <target name="compile" depends="init"
- description="Compile Java sources.">
- <javac srcdir="${jsrc}" destdir="${build}" includeJavaRuntime="yes" debug="true" target="1.5"/>
- </target>
-
- <target name="core" depends="compile"
- description="Precompile Clojure core sources.">
- <java classname="clojure.lang.Script"
- classpath="${build}:${cljsrc}">
- <sysproperty key="clojure.compile.path" value="${build}"/>
- <arg value="${precompile}"/>
- </java>
- </target>
-
- <target name="jar" depends="core"
- description="Create jar file.">
- <jar jarfile="${clojure_jar}" basedir="${build}">
- <!-- <fileset dir="${cljsrc}" includes="**/*.clj"/> -->
- <manifest>
- <attribute name="Main-Class" value="clojure.lang.Repl"/>
- <attribute name="Class-Path" value="."/>
- </manifest>
- </jar>
- </target>
-
- <target name="clean"
- description="Remove autogenerated files and directories.">
- <delete dir="${build}"/>
- </target>
+ <description>
+ Build with "ant jar" and then start the REPL with:
+ "java -jar clojure.jar"
+ </description>
+
+ <property name="src" location="src"/>
+ <property name="jsrc" location="${src}/jvm"/>
+ <property name="cljsrc" location="${src}/clj"/>
+ <property name="build" location="classes"/>
+ <property name="clojure_jar" location="clojure.jar"/>
+
+ <target name="init">
+ <tstamp/>
+ <mkdir dir="${build}"/>
+ </target>
+
+ <target name="compile_java" depends="init"
+ description="Compile Java sources.">
+ <javac srcdir="${jsrc}" destdir="${build}" includeJavaRuntime="yes"
+ debug="true" target="1.5"/>
+ </target>
+
+ <target name="compile_clojure" depends="compile_java"
+ description="Compile Clojure sources.">
+ <java classname="clojure.lang.Compile"
+ classpath="${build}:${cljsrc}">
+ <sysproperty key="clojure.compile.path" value="${build}"/>
+ <arg value="clojure.core"/>
+ <arg value="clojure.main"/>
+ <arg value="clojure.set"/>
+ <arg value="clojure.xml"/>
+ <arg value="clojure.zip"/>
+ <arg value="clojure.inspector"/>
+ </java>
+ </target>
+
+ <target name="jar" depends="compile_clojure"
+ description="Create jar file.">
+ <jar jarfile="${clojure_jar}" basedir="${build}">
+ <fileset dir="${cljsrc}" includes="**/*.clj"/>
+ <manifest>
+ <attribute name="Main-Class" value="clojure.main"/>
+ <attribute name="Class-Path" value="."/>
+ </manifest>
+ </jar>
+ </target>
+
+ <target name="clean"
+ description="Remove autogenerated files and directories.">
+ <delete dir="${build}"/>
+ </target>
</project>
diff --git a/src/clj/clojure/main.clj b/src/clj/clojure/main.clj
new file mode 100644
index 00000000..2232633c
--- /dev/null
+++ b/src/clj/clojure/main.clj
@@ -0,0 +1,231 @@
+;; Copyright (c) Rich Hickey All rights reserved. The use and
+;; distribution terms for this software are covered by the Common Public
+;; License 1.0 (http://opensource.org/licenses/cpl.php) which can be found
+;; in the file CPL.TXT at the root of this distribution. By using this
+;; software in any fashion, you are agreeing to be bound by the terms of
+;; this license. You must not remove this notice, or any other, from this
+;; software.
+
+;; Originally contributed by Stephen C. Gilardi
+
+(ns clojure.main
+ (:import (clojure.lang Compiler Compiler$CompilerException RT)))
+
+(defmacro with-bindings
+ "Executes body in the context of thread-local bindings for several vars
+ that often need to be set!"
+ [& body]
+ (let [compile-path
+ (System/getProperty "clojure.compile.path" "classes")]
+ `(binding [*ns* *ns*
+ *warn-on-reflection* *warn-on-reflection*
+ *print-meta* *print-meta*
+ *print-length* *print-length*
+ *print-level* *print-level*
+ *compile-path* ~compile-path
+ *command-line-args* *command-line-args*
+ *1 nil
+ *2 nil
+ *3 nil
+ *e nil]
+ ~@body)))
+
+(defn- root-cause
+ "Returns the initial cause of an exception or error by peeling off all of
+ its wrappers"
+ [throwable]
+ (loop [cause throwable]
+ (if-let [cause (.getCause cause)]
+ (recur cause)
+ cause)))
+
+(defn repl-exception
+ "Returns CompilerExceptions in tact, but only the root cause of other
+ throwables"
+ [throwable]
+ (if (instance? Compiler$CompilerException throwable)
+ throwable
+ (root-cause throwable)))
+
+(defn repl
+ "Generic, reusable, read-eval-print loop. Options are sequential
+ keyword-value pairs. Available options and their defaults:
+
+ - :init, function of no arguments, initialization hook
+ default: #()
+
+ - :prompt, function of no arguments, prompts for more input
+ default: #(printf \"%s=> \" (ns-name *ns*))
+
+ - :flush, function of no arguments, flushes output
+ default: flush
+
+ - :read, function of one argument, returns the next object read from
+ the input, or its argument iff the input is exhausted
+ default: #(read *in* false %)
+
+ - :eval, funtion of one argument, returns the evaluation of its
+ argument
+ default: eval
+
+ - :print, function of one argument, prints its argument to the output
+ default: println
+
+ - :caught, function of one argument, a throwable, called when
+ read, eval, or print throws an exception or error
+ default: #(.println *err* (repl-exception %))"
+ [& options]
+ (let [{:keys [init prompt flush read eval print caught]
+ :or {init #()
+ prompt #(printf "%s=> " (ns-name *ns*))
+ flush flush
+ read #(read *in* false %)
+ eval eval
+ print println
+ caught #(.println *err* (repl-exception %))}}
+ (apply hash-map options)
+ eof (Object.)]
+ (with-bindings
+ (init)
+ (loop []
+ (prompt)
+ (flush)
+ (when-not
+ (= eof
+ (try
+ (let [input (read eof)]
+ (if (= input eof)
+ eof
+ (let [value (eval input)]
+ (print value)
+ (set! *3 *2)
+ (set! *2 *1)
+ (set! *1 value))))
+ (catch Throwable e
+ (caught e)
+ (set! *e e))))
+ (recur))))))
+
+(defn load-script
+ "Loads Clojure source from a file or resource given its path. Paths
+ beginning with @ or @/ are considered relative to classpath."
+ [path]
+ (if (.startsWith path "@")
+ (RT/loadResourceScript
+ (.substring path (if (.startsWith path "@/") 2 1)))
+ (Compiler/loadFile path)))
+
+(defn- init-opt
+ "Load a script"
+ [path]
+ (load-script path))
+
+(defn- eval-opt
+ "Eval expr, print the result if it's not nil"
+ [expr]
+ (let [value (with-in-str expr (eval (read)))]
+ (when-not (nil? value)
+ (println value))))
+
+(defn- init-dispatch
+ "Returns the handler associated with an init opt"
+ [opt]
+ ({"-i" init-opt
+ "--init" init-opt
+ "-e" eval-opt
+ "--eval" eval-opt} opt))
+
+(defn- initialize
+ "Common initialize routine for repl, script, and null opts"
+ [args inits]
+ (in-ns 'user)
+ (set! *command-line-args* args)
+ (doseq [[opt arg] inits]
+ ((init-dispatch opt) arg)))
+
+(defn- repl-opt
+ "Start a repl with args and inits. Print greeting if no eval options were
+ present"
+ [[_ & args] inits]
+ (when-not (some #(= eval-opt (init-dispatch (first %))) inits)
+ (println "Clojure"))
+ (repl :init #(initialize args inits))
+ (prn))
+
+(defn- script-opt
+ "Run a script from a file, resource, or standard in with args and inits"
+ [[path & args] inits]
+ (with-bindings
+ (initialize args inits)
+ (if (= path "-")
+ (load-reader *in*)
+ (load-script path))))
+
+(defn- null-opt
+ "No repl or script opt present, just bind nil and run inits"
+ [args inits]
+ (with-bindings
+ (initialize args inits)))
+
+(defn- help-opt
+ "Print help text for main"
+ [_ _]
+ (println
+"Usage: java -jar clojure.jar [option*] [arg*]
+
+ With no options or args, runs an interactive Read-Eval-Print Loop
+
+init options:
+
+ -i, --init path Load a file or resource
+ -e, --eval expr Evaluate an expression and print its value if non-nil
+
+main options:
+
+ -r, --repl Run a repl
+ path Run a script from from a file or resource
+ - Run a script from standard input
+ -h, -?, --help Print this help message and exit
+
+operation:
+
+ - Establishes thread-local bindings for commonly set!-able vars
+ - Enters the user namespace
+ - Binds *command-line-args* to a seq of strings containing command line
+ args that appear after any main option
+ - Runs all init options in order
+ - Runs a repl or script if requested
+
+ The init options may be repeated and mixed freely, but must appear before
+ any main option. The appearance of any eval option before running a repl
+ suppresses the usual repl greeting message: \"Clojure\".
+
+ Paths may be absolute or relative in the filesystem or relative to
+ classpath. Classpath-relative paths have prefix of @ or @/"))
+
+(defn- main-dispatch
+ "Returns the handler associated with a main option"
+ [opt]
+ (or
+ ({"-r" repl-opt
+ "--repl" repl-opt
+ nil null-opt
+ "-h" help-opt
+ "--help" help-opt
+ "-?" help-opt} opt)
+ script-opt))
+
+(defn- -main
+ "Flexible main for Clojure"
+ [& args]
+ (try
+ (if args
+ (loop [[opt arg & more :as args] args inits []]
+ (if (init-dispatch opt)
+ (recur more (conj inits [opt arg]))
+ ((main-dispatch opt) args inits)))
+ (repl-opt nil nil))
+ (catch Exception e
+ (.printStackTrace e *err*))
+ (finally
+ (flush))))
diff --git a/src/clj/precompile.clj b/src/clj/precompile.clj
deleted file mode 100644
index 0f17a58d..00000000
--- a/src/clj/precompile.clj
+++ /dev/null
@@ -1,11 +0,0 @@
-;; This script is run by the Ant build task to precompile the core
-;; Clojure source files.
-
-(println "Compiling Clojure core sources...")
-
-(binding [*compile-path* (System/getProperty "clojure.compile.path")]
- (compile 'clojure.core)
- (compile 'clojure.set)
- (compile 'clojure.xml)
- (compile 'clojure.zip)
- (compile 'clojure.inspector))
diff --git a/src/jvm/clojure/lang/Compile.java b/src/jvm/clojure/lang/Compile.java
new file mode 100644
index 00000000..d31b3569
--- /dev/null
+++ b/src/jvm/clojure/lang/Compile.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
+ * which can be found in the file CPL.TXT at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ **/
+
+
+package clojure.lang;
+
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.IOException;
+
+// Compiles libs and generates class files stored within the directory
+// named by the Java System property "clojure.compile.path". Arguments are
+// strings naming the libs to be compiled. The libs and compile-path must
+// all be within CLASSPATH.
+
+public class Compile{
+
+private static final String PATH_PROP = "clojure.compile.path";
+private static final Var compile_path = RT.var("clojure.core", "*compile-path*");
+private static final Var compile = RT.var("clojure.core", "compile");
+
+public static void main(String[] args) throws Exception{
+
+ OutputStreamWriter out = (OutputStreamWriter) RT.OUT.get();
+ PrintWriter err = (PrintWriter) RT.ERR.get();
+ String path = System.getProperty(PATH_PROP);
+ int count = args.length;
+
+ if(path == null)
+ {
+ err.println("ERROR: Must set system property " + PATH_PROP +
+ "\nto the location for compiled .class files." +
+ "\nThis directory must also be on your CLASSPATH.");
+ System.exit(1);
+ }
+
+ try
+ {
+ Var.pushThreadBindings(RT.map(compile_path, path));
+
+ out.write("Compiling " + count + " " +
+ ((count == 1) ? "lib" : "libs") +
+ " to " + path);
+ out.flush();
+
+ for(String lib : args)
+ compile.invoke(Symbol.intern(lib));
+
+ Var.popThreadBindings();
+ }
+ finally
+ {
+ try
+ {
+ out.flush();
+ out.close();
+ }
+ catch(IOException e)
+ {
+ e.printStackTrace(err);
+ }
+ }
+}
+}