Clojureでタスクランナーを書く
Clojure で task runner を書く - c4se記:さっちゃんですよ☆
ClojureClojure.iconでタスクランナーを書く
code:sh
clojure tasks.clj help
rlwrap -c -b "(){}[],^%$#@\"\";:''|\\" clojure tasks.clj repl
code:deps.edn
{:deps {
cljfmt {:mvn/version "0.6.7"}
org.clojure/core.async {:mvn/version "1.1.587"}}}
code:tasks.clj
;#!/usr/bin/env clojure
(require 'cljfmt.main :as cljfmt)
(require '[clojure.core.async :as async :refer >!!])
(require 'clojure.java.shell :as shell)
(require 'clojure.string :as string)
(def tasks (ref {}))
(defmacro deftask task-name doc & body
(dosync (alter tasks assoc task-name {:doc doc :body (conj body 'do)}))
nil)
(defn act task-name
(let [tasks @tasks
task-name (symbol task-name)]
(if (contains? tasks task-name)
(->> tasks task-name :body eval)
(do (println (format "Task %s not found." task-name))
(System/exit 1)))))
(defn help []
(println (format "%-16s\t%s" 'help "Show this help."))
(println (format "%-16s\t%s" 'repl "Start a REPL."))
(doseq [task-name task @tasks]
(println (format "%-16s\t%s" task-name (:doc task)))))
(defn repl []
(println "(help) to print a help.")
(println "(act :TASK-NAME) to run the task.")
(help)
(clojure.main/repl))
(defn sh args
(println (clojure.string/join " " (conj args "+")))
(let [{:keys exit out err} (apply shell/sh args)]
(if (= 0 exit)
(print err out)
(do (print (format "Exit with %d\n" exit) out err)
(System/exit exit)))))
(defn task-parallel provider consumer
(let [nproc (.availableProcessors (Runtime/getRuntime))
chan (async/chan (* nproc 2))
threads (into []
(map (fn _
(let [on-exit (async/chan)
thread (async/go-loop [data _ (async/alts! chan (async/timeout 60000))]
(if data
(do (consumer data)
(recur (async/alts! chan (async/timeout 3000))))
(async/close! on-exit)))]
{:on-exit on-exit :thread thread}))
(range (* nproc 2))))]
(provider chan)
(async/close! chan)
(doseq [{:keys on-exit thread} threads]
(<!! on-exit)
(async/close! thread))))
(deftask format
"Format all stuffs."
(println "+ clojure -m cljfmt.main fix .")
(cljfmt/fix "." (cljfmt/merge-default-options {}))
(sh "npx" "prettier" "--write" "package.json")
(sh "npx" "prettier" "--parser" "yaml" "--write" ".yamllint" ".github/workflows/*.yml")
(task-parallel (fn chan
(>!! chan "./README.md")
(doseq [dir (filter (fn file (and (.isDirectory file) (not (re-find #"^." (.getPath file)))))
(.listFiles (io/file ".")))
path (filter (fn file (and (.isFile file) (string/ends-with? (.getPath file) ".md")))
(file-seq dir))]
(>!! chan (.getPath path))))
(fn path
(sh "npx" "prettier" "--write" path)
(sh "npx" "textlint" "--fix" path))))
(defn main argv
(cond
(or (empty? argv) (some #(= % "help") argv)) (help)
(some #(= % "repl") argv) (repl)
:else
(do (doseq task-name argv (act task-name))
(System/exit 0))))
(main (vec *command-line-args*))
helpはMakefileのを移植したもの
code:Makefile
.PHONY: help
help:
@awk -F':.*##' '/^-_a-zA-Z0-9+:.*##/{printf"%-16s\t%s\n",$$1,$$2}' $(MAKEFILE_LIST) | sort
.PHONY: example
format: ## Format all stuffs.
clojure -m cljfmt.main fix .
npx prettier --write package.json
npx prettier --parser yaml --write .yamllint .github/workflows/*.yml
ag -g '\.md$' | xargs -t -P "$(($(nproc) * 2))" -I {} sh -eux -c 'npx prettier --write "{}" && npx textlint --fix "{}"'
PythonPython.icon版は