@@ -50,7 +50,7 @@ Usage: bbin <command>
50
50
(defn now []
51
51
(Date. ))
52
52
53
- (def ^:dynamic *bbin-root* (fs/expand-home " ~/ .bbin" ))
53
+ (def ^:dynamic *bbin-root* (fs/expand-home ( str " ~ " fs/file-separator " .bbin" ) ))
54
54
55
55
(defn bbin-root [_]
56
56
*bbin-root*)
@@ -66,6 +66,11 @@ Usage: bbin <command>
66
66
(defn ensure-bbin-dirs [cli-opts]
67
67
(fs/create-dirs (bin-dir cli-opts)))
68
68
69
+ (def windows?
70
+ (some-> (System/getProperty " os.name" )
71
+ (str/lower-case )
72
+ (str/index-of " win" )))
73
+
69
74
(defn print-version [& {:as opts}]
70
75
(if (:help opts)
71
76
(print-help )
@@ -105,16 +110,69 @@ Usage: bbin <command>
105
110
[clojure.pprint :as pprint]
106
111
[selmer.parser :as selmer]
107
112
[selmer.util :as selmer-util]
113
+ [selmer.filters :as filters]
108
114
[babashka.bbin.util :as util :refer [sh]]))
109
115
110
116
(defn- pprint [x _]
111
117
(pprint/pprint x))
112
118
113
119
(defn- gitlib-path [cli-opts script-deps]
114
120
(let [coords (val (first script-deps))]
115
- (fs/expand-home (str " ~/.gitlibs/libs/" (:script/lib cli-opts) " /" (:git/sha coords)))))
121
+ (fs/expand-home (str/join fs/file-separator [" ~" " .gitlibs" " libs" (:script/lib cli-opts) (:git/sha coords)]))))
122
+
123
+ ; windows scripts are babashka/clojure instead of shell scripts, so we need a variable
124
+ ; comment character in the script meta
125
+ (def ^:private comment-char
126
+ (if util/windows? " ; " " # " ))
127
+
128
+ (def windows-wrapper-extension " .bat" )
129
+
130
+ ; selmer filter for clojure escaping for e.g. files
131
+ (filters/add-filter! :pr-str (comp pr-str str))
116
132
117
133
(def ^:private tool-template-str
134
+ (if util/windows?
135
+ " #!/usr/bin/env bb
136
+ ; :bbin/start
137
+ ;
138
+ {{script/meta}}
139
+ ; :bbin/end
140
+ (require '[babashka.process :as process]
141
+ '[babashka.fs :as fs]
142
+ '[clojure.string :as str])
143
+
144
+ (let [SCRIPT_ROOT {{script/root|pr-str}}
145
+ SCRIPT_LIB '{{script/lib}}
146
+ SCRIPT_COORDS {{script/coords|str}}
147
+ SCRIPT_NS_DEFAULT '{{script/ns-default}}
148
+ SCRIPT_NAME (fs/file-name *file*)
149
+ TMP_EDN (doto (fs/file (fs/temp-dir) (str (gensym \" bbin\" )))
150
+ (spit (str \" {:deps {\" SCRIPT_LIB SCRIPT_COORDS \" }}\" ))
151
+ (fs/delete-on-exit))
152
+ [first-arg & rest-args] *command-line-args*]
153
+ (if first-arg
154
+ (process/exec
155
+ (str/join \" \" (concat [\" bb --deps-root \" SCRIPT_ROOT \" --config\" (str TMP_EDN)
156
+ \" -x\" (str SCRIPT_NS_DEFAULT \" /\" first-arg)
157
+ \" --\" ] rest-args)))
158
+ (do
159
+ (let [script (str \" (require '\" SCRIPT_NS_DEFAULT \" )
160
+ (def fns (filter #(fn? (deref (val %))) (ns-publics '\" SCRIPT_NS_DEFAULT \" )))
161
+ (def max-width (->> (keys fns) (map (comp count str)) (apply max)))
162
+ (defn pad-right [x] (format (str \\\" %-\\\" max-width \\\" s\\\" ) x))
163
+ (println (str \\\" Usage: \" SCRIPT_NAME \" <command>\\\" ))
164
+ (newline)
165
+ (doseq [[k v] fns]
166
+ (println
167
+ (str \\\" \" SCRIPT_NAME \" \\\" (pad-right k) \\\" \\\"
168
+ (when (:doc (meta v))
169
+ (first (str/split-lines (:doc (meta v))))))))\" )
170
+ cmd-line [\" bb\" \" --deps-root\" SCRIPT_ROOT \" --config\" (str TMP_EDN)
171
+ \" -e\" script]]
172
+ (process/exec cmd-line)))))"
173
+ ;
174
+ ; non-windows tool script
175
+ ;
118
176
" #!/usr/bin/env bash
119
177
set -e
120
178
@@ -152,9 +210,36 @@ else
152
210
--config <(echo \" {:deps {$SCRIPT_LIB $SCRIPT_COORDS}}\" ) \\
153
211
-x $SCRIPT_NS_DEFAULT/$1 \\
154
212
-- \" ${@:2}\"
155
- fi" )
213
+ fi" ))
156
214
157
215
(def ^:private git-or-local-template-str
216
+ (if util/windows?
217
+ " #!/usr/bin/env bb
218
+ ; :bbin/start
219
+ ;
220
+ {{script/meta}}
221
+ ; :bbin/end
222
+
223
+ (require '[babashka.process :as process]
224
+ '[babashka.fs :as fs]
225
+ '[clojure.string :as str])
226
+
227
+ (let [SCRIPT_ROOT {{script/root|pr-str}}
228
+ SCRIPT_LIB '{{script/lib}}
229
+ SCRIPT_COORDS {{script/coords|str}}
230
+ SCRIPT_MAIN_OPTS_FIRST {{script/main-opts.0|pr-str}}
231
+ SCRIPT_MAIN_OPTS_SECOND {{script/main-opts.1|pr-str}}
232
+ TMP_EDN (doto (fs/file (fs/temp-dir) (str (gensym \" bbin\" )))
233
+ (spit (str \" {:deps {\" SCRIPT_LIB SCRIPT_COORDS\" }}\" ))
234
+ (fs/delete-on-exit))]
235
+
236
+ (process/exec
237
+ (str/join \" \" (concat [\" bb --deps-root\" SCRIPT_ROOT \" --config\" (str TMP_EDN)
238
+ SCRIPT_MAIN_OPTS_FIRST SCRIPT_MAIN_OPTS_SECOND
239
+ \" --\" ] *command-line-args*))))"
240
+ ;
241
+ ; non-windows script template
242
+ ;
158
243
" #!/usr/bin/env bash
159
244
set -e
160
245
@@ -174,7 +259,7 @@ exec bb \\
174
259
--deps-root \" $SCRIPT_ROOT\" \\
175
260
--config <(echo \" {:deps {$SCRIPT_LIB $SCRIPT_COORDS}}\" ) \\
176
261
$SCRIPT_MAIN_OPTS_FIRST \" $SCRIPT_MAIN_OPTS_SECOND\" \\
177
- -- \" $@\" " )
262
+ -- \" $@\" " ))
178
263
179
264
(defn- http-url->script-name [http-url]
180
265
(first
@@ -201,6 +286,23 @@ exec bb \\
201
286
code)]
202
287
(str/join " \n " next-lines)))
203
288
289
+ (defn- install-script
290
+ " Spits `contents` to `path` (adding an extension on Windows), or
291
+ pprints them if `dry-run?` is truthy.
292
+ Side-effecting."
293
+ [path contents dry-run?]
294
+ (let [path-str (str path)]
295
+ (if dry-run?
296
+ (pprint {:script-file path-str
297
+ :script-contents contents}
298
+ dry-run?)
299
+ (do
300
+ (spit path-str contents)
301
+ (when-not util/windows? (sh [" chmod" " +x" path-str]))
302
+ (when util/windows?
303
+ (spit (str path-str windows-wrapper-extension) (str " @bb -f %~dp0" (fs/file-name path-str) " -- %*" )))
304
+ nil ))))
305
+
204
306
(defn- install-http [cli-opts]
205
307
(let [http-url (:script/lib cli-opts)
206
308
script-deps {:bbin/url http-url}
@@ -211,14 +313,7 @@ exec bb \\
211
313
(insert-script-header header))
212
314
script-file (fs/canonicalize (fs/file (util/bin-dir cli-opts) script-name)
213
315
{:nofollow-links true })]
214
- (if (:dry-run cli-opts)
215
- (pprint {:script-file (str script-file)
216
- :script-contents script-contents}
217
- cli-opts)
218
- (do
219
- (spit (str script-file) script-contents)
220
- (sh [" chmod" " +x" (str script-file)])
221
- nil ))))
316
+ (install-script script-file script-contents (:dry-run cli-opts))))
222
317
223
318
(defn- default-script-config [cli-opts]
224
319
(let [[ns name] (str/split (:script/lib cli-opts) #"/" )
@@ -253,7 +348,7 @@ exec bb \\
253
348
(:main-opts script-config))
254
349
template-opts {:script/meta (->> script-edn-out
255
350
str/split-lines
256
- (map #(str " # " %))
351
+ (map #(str comment-char %))
257
352
(str/join " \n " ))
258
353
:script/root script-root
259
354
:script/lib (pr-str (key (first script-deps)))
@@ -272,16 +367,34 @@ exec bb \\
272
367
template-out (selmer-util/without-escaping
273
368
(selmer/render template-str template-opts'))
274
369
script-file (fs/canonicalize (fs/file (util/bin-dir cli-opts) script-name) {:nofollow-links true })]
275
- (if (:dry-run cli-opts)
276
- (pprint {:script-file (str script-file)
277
- :template-out template-out}
278
- cli-opts)
279
- (do
280
- (spit (str script-file) template-out)
281
- (sh [" chmod" " +x" (str script-file)])
282
- nil ))))
370
+ (install-script script-file template-out (:dry-run cli-opts))))
283
371
284
372
(def ^:private maven-template-str
373
+ (if util/windows?
374
+ " #!/usr/bin/env bb
375
+ ; :bbin/start
376
+ ;
377
+ {{script/meta}}
378
+ ; :bbin/end
379
+ (require '[babashka.process :as process]
380
+ '[babashka.fs :as fs]
381
+ '[clojure.string :as str])
382
+
383
+ (let [SCRIPT_LIB '{{script/lib}}
384
+ SCRIPT_COORDS {{script/coords|str}}
385
+ SCRIPT_MAIN_OPTS_FIRST {{script/main-opts.0|pr-str}}
386
+ SCRIPT_MAIN_OPTS_SECOND {{script/main-opts.1|pr-str}}
387
+ TMP_EDN (doto (fs/file (fs/temp-dir) (str (gensym \" bbin\" )))
388
+ (spit (str \" {:deps {\" SCRIPT_LIB SCRIPT_COORDS \" }}\" ))
389
+ (fs/delete-on-exit))]
390
+
391
+ (process/exec
392
+ (str/join \" \" (concat [\" bb --config\" (str TMP_EDN)
393
+ SCRIPT_MAIN_OPTS_FIRST SCRIPT_MAIN_OPTS_SECOND
394
+ \" --\" ] *command-line-args*))))\" "
395
+ ;
396
+ ; non-windows script template
397
+ ;
285
398
" #!/usr/bin/env bash
286
399
set -e
287
400
@@ -299,7 +412,7 @@ SCRIPT_MAIN_OPTS_SECOND='{{script/main-opts.1}}'
299
412
exec bb \\
300
413
--config <(echo \" {:deps {$SCRIPT_LIB $SCRIPT_COORDS}}\" ) \\
301
414
$SCRIPT_MAIN_OPTS_FIRST \" $SCRIPT_MAIN_OPTS_SECOND\" \\
302
- -- \" $@\" " )
415
+ -- \" $@\" " ))
303
416
304
417
(defn- install-deps-maven [cli-opts]
305
418
(let [script-deps {(edn/read-string (:script/lib cli-opts))
@@ -318,7 +431,7 @@ exec bb \\
318
431
(:main-opts script-config))
319
432
template-opts {:script/meta (->> script-edn-out
320
433
str/split-lines
321
- (map #(str " # " %))
434
+ (map #(str comment-char %))
322
435
(str/join " \n " ))
323
436
:script/root script-root
324
437
:script/lib (pr-str (key (first script-deps)))
@@ -331,14 +444,7 @@ exec bb \\
331
444
template-out (selmer-util/without-escaping
332
445
(selmer/render maven-template-str template-opts))
333
446
script-file (fs/canonicalize (fs/file (util/bin-dir cli-opts) script-name) {:nofollow-links true })]
334
- (if (:dry-run cli-opts)
335
- (pprint {:script-file (str script-file)
336
- :template-out template-out}
337
- cli-opts)
338
- (do
339
- (spit (str script-file) template-out)
340
- (sh [" chmod" " +x" (str script-file)])
341
- nil ))))
447
+ (install-script script-file template-out (:dry-run cli-opts))))
342
448
343
449
(defn- parse-script [s]
344
450
(let [lines (str/split-lines s)
@@ -356,6 +462,7 @@ exec bb \\
356
462
(filter #(.isFile %))
357
463
(map (fn [x] [(symbol (str (fs/relativize (util/bin-dir cli-opts) x)))
358
464
(parse-script (slurp x))]))
465
+ (filter second)
359
466
(into {})))
360
467
361
468
(defn ls [cli-opts]
@@ -386,6 +493,7 @@ exec bb \\
386
493
(let [script-name (:script/lib cli-opts)
387
494
script-file (fs/canonicalize (fs/file (util/bin-dir cli-opts) script-name) {:nofollow-links true })]
388
495
(when (fs/delete-if-exists script-file)
496
+ (when util/windows? (fs/delete-if-exists (fs/file (str script-file windows-wrapper-extension))))
389
497
(println " Removing" (str script-file)))))))
390
498
391
499
(ns babashka.bbin.cli
0 commit comments