-
Notifications
You must be signed in to change notification settings - Fork 0
/
shadow.clj
205 lines (174 loc) · 6.57 KB
/
shadow.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
(ns services.shadow
(:require [clojure.java.io :as io]
[clojure.java.shell :as shell]
[clojure.tools.cli :refer [parse-opts]]
[clojure.tools.logging :as log]
[plumbing.core :refer :all]
[puppetlabs.trapperkeeper.core :as tk]
[shadow.cljs.build :as cljs]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; React
(defn compose-externs [externs]
(conj externs "react/externs/react.js"))
;; from the React jar file
(defn react-js-source [mode]
(let [load-resource #(-> % io/resource slurp)]
(case mode
:debug (load-resource "react/react.js")
:production (load-resource "react/react.min.js"))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Shadow Logging
(defn shadow-logger []
(reify cljs/BuildLog
(log-warning [_ log-string]
(log/info log-string))
(log-progress [_ log-string]
(log/info log-string))
(log-time-start [_ log-string])
(log-time-end [_ log-string time-in-ms]
(log/info log-string time-in-ms))))
(defn configure-logging [state]
(assoc state :logger (shadow-logger)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Build Steps
(defn compiler-options [state {:keys [mode externs public-path target-path]}]
(-> state
(into {:public-path public-path
:public-dir (io/file target-path)
:work-dir (io/file (str target-path "-work"))})
(into (case mode
:debug {:optimizations :none
:pretty-print true}
:production {:optimizations :advanced
:pretty-print false
:externs (compose-externs externs)}))))
(defn configure-source-paths [state source-paths]
(reduce (fn [state source-path]
(cljs/step-find-resources state source-path))
state
source-paths))
(defn configure-core-module [state core-libs mode]
(cljs/step-configure-module
state :core core-libs #{} {:prepend (react-js-source mode)}))
(defn configure-modules [state {:keys [mode core-libs modules] :as opts}]
(let [state (configure-core-module state core-libs mode)]
(reduce (fn [state {:keys [id main]}]
(cljs/step-configure-module state id [main] #{:core}))
state
modules)))
(defnk build-state [source-paths :as opts]
(-> (cljs/init-state)
configure-logging
cljs/enable-source-maps
(compiler-options opts)
cljs/step-find-resources-in-jars
(configure-source-paths source-paths)
cljs/step-finalize-config
cljs/step-compile-core
(configure-modules opts)))
(defmulti compile-step #(:mode %2))
(defmethod compile-step :debug [state _]
(-> state
cljs/step-compile-modules
cljs/flush-unoptimized))
(defmethod compile-step :production [state _]
(-> state
cljs/step-compile-modules
cljs/closure-optimize
cljs/flush-modules-to-disk))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Build modes
(defn extract-shadow [project-file]
(->> project-file (drop 3) (apply hash-map) :shadow))
(defn project-config [project-root]
(-> (str project-root "/project.clj")
slurp
read-string
extract-shadow
(assoc :root project-root)))
(defn prepare-opts [opts mode]
(let [root #(-> opts :root (str "/" %))
suffix (if (= :debug mode) "-debug")
add-suffix #(str % suffix)]
(-> opts
(assoc :mode mode :source-paths [(root "src") (root "target/classes")])
(update-in [:externs] (partial map root))
(update-in [:public-path] add-suffix)
(update-in [:target-path] (comp root add-suffix))
(dissoc :root))))
(defn prepare-project [project build]
(-> project project-config (prepare-opts build)))
(defn build-once [opts]
(-> (build-state opts)
(compile-step opts))
(log/info "BUILD COMPLETE")
:build-completed)
(defn delete-folder [path]
(log/info "Deleting path " path "...")
(shell/sh "bash" "-c" (str "rm -rf " path)))
(defn clean-project [project-cfg]
(delete-folder (:target-path project-cfg)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Service
(tk/defservice service
[[:ConfigService get-in-config]]
(start [this context]
(let [modes (get-in-config [:shadow])
context (assoc context :active true)]
(log/info "Initializing Shadow service for build modes" modes)
(doseq [mode modes]
(.start
(Thread.
(fn []
(let [opts (prepare-project "." mode)
state (build-state opts)]
(loop [state state]
(recur
(do
(try
(when (:active context)
(-> state
(compile-step opts)
cljs/wait-and-reload!))
(catch Throwable t
(log/error "Failed to compile: " t)
(.printStackTrace t)
(cljs/wait-and-reload! state))))))
(log/info "Stopping Shadow builds for" (:mode opts))))))))
context)
(stop [this context]
(dissoc context :builds :active)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Command line
(defn build-auto [opts]
(let [state (build-state opts)]
(loop [state state]
(recur
(try
(-> state
(compile-step opts)
cljs/wait-and-reload!)
(catch Throwable t
(log/error "Failed to compile: " t)
(.printStackTrace t)
(cljs/wait-and-reload! state)))))))
(defnk do-build [build mode {clean nil}]
(let [project-cfg (prepare-project "." build)
state (build-state project-cfg)]
(log/info "Build" build mode "...")
(if clean (clean-project project-cfg))
(case mode
:once (build-once project-cfg)
:auto (build-auto project-cfg))))
(def cli-options
[["-c" "--clean" "Clean target first."]
["-b" "--build debug|production|both" "Build type. Default is debug."
:default :debug
:parse-fn keyword
:validate [#{:debug :production :both} "Must be either debug, production or both."]]
["-m" "--mode once|auto" "Build once or auto. Default is once."
:default :once
:parse-fn keyword
:validate [#{:once :auto} "Must be either once or auto"]]])
(defn -main [& args]
(do-build (:options (parse-opts args cli-options))))