Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 287 lines (242 sloc) 8.83 KB
exec java -server clojure.main -e \
; Dependency: START
; License
; Copyright (c) Rich Hickey and contributors. All rights reserved.
; The use and distribution terms for this software are covered by the
; Eclipse Public License 1.0 (
; which can be found in the file epl.html 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.
(ns ^{:author \"Gareth Jones\"}
(:use [clojure.string :only (replace)]
[clojure.pprint :only (pprint cl-format)])
(:refer-clojure :exclude [replace]))
(defn- build-doc [{:keys [switches docs default]}]
[(apply str (interpose \", \" switches))
(or (str default) \"\")
(or docs \"\")])
(defn- banner-for [specs]
(println \"Usage:\")
(let [docs (into (map build-doc specs)
[[\"--------\" \"-------\" \"----\"]
[\"Switches\" \"Default\" \"Desc\"]])
max-cols (->> (for [d docs] (map count d))
(apply map (fn [& c] (apply vector c)))
(map #(apply max %)))
vs (for [d docs]
(mapcat (fn [& x] (apply vector x)) max-cols d))]
(doseq [v vs]
(cl-format true \"~{ ~vA ~vA ~vA ~}\" v)
(defn- name-for [k]
(replace k #\"^--no-|^--\\[no-\]|^--|^-\" \"\"))
(defn- flag-for [^String v]
(not (.startsWith v \"--no-\")))
(defn- opt? [^String x]
(.startsWith x \"-\"))
(defn- flag? [^String x]
(.startsWith x \"--[no-]\"))
(defn- end-of-args? [x]
(= \"--\" x))
(defn- spec-for
[arg specs]
(->> specs
(filter (fn [s]
(let [switches (set (s :switches))]
(contains? switches arg))))
(defn- default-values-for
(into {} (for [s specs] [(s :name) (s :default)])))
(defn- apply-specs
[specs args]
(loop [options (default-values-for specs)
extra-args []
args args]
(if-not (seq args)
[options extra-args]
(let [opt (first args)
spec (spec-for opt specs)]
(end-of-args? opt)
(recur options (into extra-args (vec (rest args))) nil)
(and (opt? opt) (nil? spec))
(throw (Exception. (str \"'\" opt \"' is not a valid argument\")))
(and (opt? opt) (spec :flag))
(recur (assoc options (spec :name) (flag-for opt))
(rest args))
(opt? opt)
(recur (assoc options (spec :name) ((spec :parse-fn) (second args)))
(drop 2 args))
(recur options (conj extra-args (first args)) (rest args)))))))
(defn- switches-for
[switches flag]
(-> (for [^String s switches]
(and flag (flag? s)) [(replace s #\"\\[no-\]\" \"no-\") (replace s #\"\[no-\]\" \"\")]
(and flag (.startsWith s \"--\")) [(replace s #\"--\" \"--no-\") s]
:default [s]))
(defn- generate-spec
(let [[switches raw-spec] (split-with #(and (string? %) (opt? %)) raw-spec)
[docs raw-spec] (split-with string? raw-spec)
options (apply hash-map raw-spec)
aliases (map name-for switches)
flag (or (flag? (last switches)) (options :flag))]
(merge {:switches (switches-for switches flag)
:docs (first docs)
:aliases (set aliases)
:name (keyword (last aliases))
:parse-fn identity
:default (if flag false nil)
:flag flag}
(defn cli
\"Parse the provided args using the given specs. Specs are vectors
describing a command line argument. For example:
[\\\"-p\\\" \\\"--port\\\" \\\"Port to listen on\\\" :default 3000 :parse-fn #(Integer/parseInt %)]
First provide the switches (from least to most specific), then a doc
string, and pairs of options.
Valid options are :default, :parse-fn, and :flag. See for more
detailed examples.
Returns a vector containing a map of the parsed arguments, a vector
of extra arguments that did not match known switches, and a
documentation banner to provide usage instructions.\"
[args & specs]
(let [specs (map generate-spec specs)]
(let [[options extra-args] (apply-specs specs args)
banner (with-out-str (banner-for specs))]
[options extra-args banner])))
\"initialized tools cli\"
(ns ^{:author \"ibdknox (Chris Granger)\"} cljs_watch)
(use '[ :only [file]])
(require '[cljs.closure :as cljsc])
(alias 'cli '
(import '[java.util Calendar])
(import '[java.text SimpleDateFormat])
(defn text-timestamp []
(let [c (Calendar/getInstance)
f (SimpleDateFormat. \"HH:mm:ss\")]
(.format f (.getTime c))))
(def default-opts {:output-dir \"resources/public/cljs/\"
:output-to \"resources/public/cljs/bootstrap.js\"})
{:reset \"[0m\"
:default \"[39m\"
:white \"[37m\"
:black \"[30m\"
:red \"[31m\"
:green \"[32m\"
:blue \"[34m\"
:yellow \"[33m\"
:magenta \"[35m\"
:cyan \"[36m\"
(defn ansi
(str \u001b (get ANSI-CODES code (:reset ANSI-CODES))))
(defn style
[s & codes]
(str (apply str (map ansi codes)) s (ansi :reset)))
(def last-compiled (atom 0))
(defn ext-filter [coll ext]
(filter (fn [f]
(let [fname (.getName f)
fext (subs fname (inc (.lastIndexOf fname \".\")))]
(and (not= \. (first fname)) (.isFile f) (= fext ext))))
(defn find-cljs [dir]
(let [dir-files (-> dir file file-seq)]
(ext-filter dir-files \"cljs\")))
(defn compile-cljs [src-dir opts]
(cljsc/build src-dir opts)
(catch Throwable e
(.printStackTrace e)))
(reset! last-compiled (System/currentTimeMillis)))
(defn newer? [f]
(let [last-modified (.lastModified f)]
(> last-modified @last-compiled)))
(defn files-updated? [dir]
(some newer? (find-cljs dir)))
(defn watcher-print [& text]
(print (style (str (text-timestamp) \" :: watcher :: \") :magenta))
(apply print text)
(defn status-print [text]
(print \" \" (style text :green) \"\n\")
(defn- parse-options [extra]
(let [opts-string (apply str (interpose \" \" extra))]
(if (empty? opts-string)
(try (let [opts (read-string opts-string)]
(if (map? opts)
(println \"cljs options must be in map syntax\")
(catch Exception e (println e))))))
(defn nop [])
(defn- create-done-fn [argsmap]
(if (:bell argsmap)
#(do (print \\u0007) (flush))
(if-let [cmd (:bell-cmd argsmap)]
#(.exec (Runtime/getRuntime) cmd)
(defn transform-cl-args [args]
(let [[argmap extra] (cli/cli args
[\"-s\" \"--source\" \"Source file or directory\" :default \"src/\"]
[\"-b\" \"--bell\" \"Uses system beep to indicate a finished compile\" :flag true]
[\"-c\" \"--bell-cmd\" \"Use this to customize the beep command e.g. growlnotify -m compile_done cljs-watch\"])
options (parse-options extra)]
(assoc argmap :options options)))
(let [
argsmap (transform-cl-args *command-line-args*)
{src-dir :source, :keys [options]} argsmap
donefn (create-done-fn argsmap)
opts (merge default-opts options)]
(.mkdirs (file (:output-dir opts)))
(watcher-print \"Building ClojureScript files in ::\" src-dir)
(compile-cljs src-dir opts)
(status-print \"[done]\")
(watcher-print \"Waiting for changes\n\")
(while true
(Thread/sleep 1000)
(when (files-updated? src-dir)
(watcher-print \"Compiling updated files...\")
(compile-cljs src-dir opts)
(status-print \"[done]\")
" cljs-watch "$@"
# - The quoted "$@" is important in order to handle cli arguments with spaces correctly
# - The cljs-watch is just to simulate the first parameter, stating the executed
# command itself.