Inspired by https://racket.discourse.group/t/how-to-make-small-executables/2244.
Last active
August 31, 2023 11:56
-
-
Save LiberalArtist/0297fcba633c06fe1d5138eacaeceb62 to your computer and use it in GitHub Desktop.
`raco dist` Demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/config.mk | |
/build | |
/*.tar | |
/*.html | |
/_zuo.db | |
/_zuo_tc.db | |
/compiled | |
*~ | |
\#* | |
.\#* | |
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#lang racket/base | |
42 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
exitStatus=0 | |
invoke () { | |
echo $1 | |
./$1 | |
if [ ! $? -eq 0 ]; then | |
exitStatus=1 | |
fi | |
} | |
for layout in -exe /bin | |
do | |
for label in Embedded Shared | |
do | |
for exe in foo bar | |
do | |
invoke build/${label}Collects${layout}/${exe} | |
done | |
done | |
invoke build/SingleUtility${layout}/foo | |
done | |
exit $exitStatus |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#lang racket/base | |
(displayln "Hi, world!") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#lang zuo | |
;; SPDX-License-Identifier: (Apache-2.0 OR MIT) | |
;; SPDX-FileCopyrightText: 2023 Philip McGrath <[email protected]> | |
;; SPDX-FileCopyrightText: Racket contributors (see `directory-sha256`) | |
(provide-targets targets-at) | |
(define names | |
`("foo" "bar")) | |
(define (targets-at at-dir arg-vars) | |
(define config.mk | |
(at-dir "config.mk")) | |
(define vars | |
(if (file-exists? config.mk) | |
(let ([vars (config-file->hash config.mk arg-vars)]) | |
(alert (~a (if (equal? arg-vars (hash)) | |
"Used" | |
"Extended") | |
" configuration from " | |
(~v config.mk))) | |
(for-each (lambda (k) | |
(alert (~a " " k) (hash-ref vars k))) | |
(hash-keys vars)) | |
vars) | |
arg-vars)) | |
(define raco | |
(hash-ref vars 'RACO "raco")) | |
(define raco-target | |
(input-data-target 'raco raco)) | |
(define at-build-dir | |
(make-at-dir "build")) | |
(define at-make-dir | |
(make-at-dir (at-build-dir "make"))) | |
(define (make-targets/db specs) | |
;; https://github.com/racket/racket/issues/4732 | |
(make-targets `([:db-dir ,(at-dir ".")] | |
,@specs))) | |
(define cp-targets | |
(make-targets/db | |
(map (lambda (base) | |
(define src | |
(at-source (~a base ".rkt"))) | |
`[:target ,(at-make-dir (~a base ".rkt")) (,src) | |
,(lambda (dest token) | |
(mkdir-p (at-make-dir)) | |
(cp src dest))]) | |
names))) | |
(define zo+dep-targets | |
(make-targets/db | |
`([:target ,(map (make-at-dir (at-make-dir "compiled")) | |
(append-map (lambda (base) | |
(list (~a base "_rkt.zo") | |
(~a base "_rkt.dep"))) | |
names)) | |
(,raco-target ,@cp-targets) | |
,(lambda (dest0 token) | |
(shell/wait | |
(build-shell raco "make" | |
"-j" (~a (min (length names) | |
;; FIXME | |
#; | |
(max (or (maybe-jobserver-jobs) | |
1) | |
1))) | |
"--" | |
(map target-shell cp-targets)) | |
(hash 'desc "raco make")))]))) | |
(define (make-raco-dist-targets dist-name share-collects? [single-util? #f]) | |
(define at-exe-dir | |
(make-at-dir (at-build-dir (~a dist-name "-exe")))) | |
(define exe-files | |
(map at-exe-dir (map .exe (if single-util? | |
(list (car names)) | |
names)))) | |
(define (raco-exe rkt exe [extra '()]) | |
(shell/wait | |
(build-shell raco "exe" | |
"-o" (string->shell (path-replace-extension exe "")) | |
extra | |
"--" | |
(string->shell rkt)) | |
(hash 'desc "raco exe"))) | |
(define exe-targets | |
(if share-collects? | |
(list | |
(target | |
(at-exe-dir "collects") | |
(lambda (collects-dir token) | |
(rule (cons raco-target | |
(append cp-targets | |
zo+dep-targets)) | |
(lambda () | |
(mkdir-p (at-exe-dir)) | |
(for-each rm* exe-files) | |
(map (lambda (exe rkt-target) | |
(raco-exe (target-path rkt-target) | |
exe | |
(list "--collects-path" | |
"collects" | |
"--collects-dest" | |
(string->shell collects-dir)))) | |
exe-files | |
(if single-util? | |
(list (car cp-targets)) | |
cp-targets)) | |
(unless (directory-exists? collects-dir) | |
(error (~a dist-name ": failed to create " | |
(~v collects-dir)))) | |
(directory-sha256 collects-dir token)) | |
(directory-sha256 collects-dir token))) | |
(hash 'precious? #t | |
'co-outputs exe-files))) | |
(make-targets/db | |
(map (lambda (exe rkt-target) | |
`[:target ,exe (,rkt-target ,raco-target ,@zo+dep-targets) | |
,(lambda (dest token) | |
(mkdir-p (at-exe-dir)) | |
(raco-exe (target-path rkt-target) | |
exe))]) | |
exe-files | |
(if single-util? | |
(list (car cp-targets)) | |
cp-targets))))) | |
(cons | |
(target (at-build-dir dist-name) | |
(lambda (dist-dir token) | |
(rule | |
(cons raco-target exe-targets) | |
(lambda () | |
(rm* dist-dir) | |
(shell/wait | |
(build-shell raco "dist" | |
(if share-collects? | |
(list "++collects-copy" | |
(at-exe-dir "collects")) | |
"") | |
"--" | |
(string->shell dist-dir) | |
(map string->shell exe-files)) | |
(hash 'desc "raco dist")) | |
(directory-sha256 dist-dir token)) | |
(directory-sha256 dist-dir token)))) | |
exe-targets)) | |
(define dist-target-lists | |
(list (make-raco-dist-targets "EmbeddedCollects" #f) | |
(make-raco-dist-targets "SharedCollects" #t) | |
(make-raco-dist-targets "SingleUtility" #f #t))) | |
(define dist-dir-targets | |
(map car dist-target-lists)) | |
(define dist+exe-targets | |
(apply append dist-target-lists)) | |
(define tar-targets | |
(let* ([tar (hash-ref vars 'TAR "tar --sort=name")] | |
[cmd-target (input-data-target 'tar tar)]) | |
(make-targets/db | |
(map (lambda (dist-target) | |
(define dist-path (target-path dist-target)) | |
(define dist-name (file-name-from-path dist-path)) | |
(define pwd (build-path dist-path "..")) | |
`[:target ,(at-dir (~a (file-name-from-path | |
(target-path dist-target)) | |
".tar")) | |
(,cmd-target ,dist-target) | |
,(lambda (dest token) | |
(rm* dest) | |
(shell/wait | |
(build-shell tar "-cf" | |
(string->shell | |
(find-relative-path | |
(simple-form-path pwd) | |
(simple-form-path dest))) | |
(string->shell dist-name)) | |
(hash 'desc "tar" | |
'dir pwd)))]) | |
dist-dir-targets)))) | |
(define html-targets | |
(let* ([tree (hash-ref vars 'TREE "tree")] | |
[du (hash-ref vars 'DU "du")] | |
[echo (hash-ref vars 'ECHO "echo")] | |
[data-targets (map input-data-target | |
'(tree du echo) | |
(list tree du echo))]) | |
(make-targets/db | |
(append-map | |
(lambda (dist-target) | |
(define dist-name | |
(file-name-from-path (target-path dist-target))) | |
(map (lambda (html?) | |
`[:target | |
,(at-dir (~a (if html? "" "$") | |
dist-name | |
(if html? ".html" ".md"))) | |
(,dist-target ,@data-targets) | |
,(lambda (dist-html token) | |
(define size | |
(car (string-split | |
(shell/wait/output | |
;; `tree --du` is broken for me | |
(build-shell du "-hs" (target-shell | |
dist-target)) | |
"du") | |
"\t"))) | |
(define outro | |
(~a "<p>Total " size "</p>" | |
(if html? "</body></html>" ""))) | |
(define tree-cmd | |
(build-shell | |
echo (string->shell outro) "|" | |
tree "-o" (if html? | |
(string->shell dist-html) | |
"/dev/stdout") | |
(if html? "-C" "--hintro=/dev/null") | |
"--nolinks" "--houtro=/dev/stdin" "-H" "." "-h" | |
"-T" (string->shell dist-name) | |
(target-shell dist-target))) | |
(cond | |
[html? | |
(shell/wait tree-cmd (hash 'desc "tree"))] | |
[else | |
(define lines | |
(string-split (shell/wait/output tree-cmd "tree") | |
"\n")) | |
(define trimmed | |
(map (lambda (l) | |
(if (or (equal? "" l) | |
(not (equal? (char "\t") | |
(string-ref l 0)))) | |
l | |
(substring l 1))) | |
lines)) | |
(display-to-file | |
(string-join (cons (~a "<h1>" dist-name "</h1>") | |
trimmed) | |
"\n") | |
dist-html)]))]) | |
'(#t #f))) | |
dist-dir-targets)))) | |
(append (make-targets/db | |
`([:target all (,@tar-targets ,@html-targets) | |
,void] | |
[:target tar (,@tar-targets) | |
,void] | |
[:target html (,@html-targets) | |
,void] | |
[:target md (,@html-targets) | |
,void] | |
[:target clean () | |
,(lambda (token) | |
(for-each rm* | |
(cons (at-build-dir) | |
(map target-path | |
(append tar-targets | |
html-targets)))))] | |
[:target check (,(at-source "check.sh") ,@dist+exe-targets) | |
,(lambda (token) | |
(when (eq? 'windows (hash-ref (runtime-env) | |
'system-type)) | |
(error (~a (~v (at-source "check.sh")) | |
" not supported on Windows"))) | |
(define p (process (at-source "check.sh"))) | |
(process-wait (hash-ref p 'process)) | |
(unless (= 0 (process-status (hash-ref p 'process))) | |
(error (~a (~v (at-source "check.sh")) | |
" failed"))))] | |
,@(map (lambda (dir-target . deps) | |
`[:target ,(string->symbol (file-name-from-path | |
(target-path dir-target))) | |
,deps | |
,void]) | |
dist-dir-targets | |
tar-targets))) | |
tar-targets | |
html-targets | |
dist+exe-targets | |
zo+dep-targets | |
cp-targets)) | |
(define (append-map proc lst0 . lsts) | |
(apply append (apply map (list* proc lst0 lsts)))) | |
(define (make-max prefer-first?) | |
(lambda (init . args) | |
(foldl (lambda (a b) | |
(if (prefer-first? a b) | |
a | |
b)) | |
init | |
args))) | |
(define max | |
(make-max >)) | |
(define min | |
(make-max <)) | |
(define (shell/wait/output cmd [desc "shell command"]) | |
(displayln cmd) | |
(define p (shell cmd (hash 'stdout 'pipe))) | |
(define i (hash-ref p 'stdout)) | |
(thread-process-wait (hash-ref p 'process)) | |
(unless (= 0 (process-status (hash-ref p 'process))) | |
(error (~a desc " failed"))) | |
(let ([str (fd-read i 'avail)]) | |
(fd-close i) | |
str)) | |
(define (directory-sha256 dir token) | |
;; based on `source-tree` from racket/src/ChezScheme/makefiles/lib.zuo | |
(if (directory-exists? dir) | |
(string-join | |
(let loop ([dir dir] [accum '()]) | |
(foldl (lambda (e-name accum) | |
(define e (build-path dir e-name)) | |
(define st (stat e)) | |
(define type (and st (hash-ref st 'type))) | |
(cond | |
[(eq? type 'file) (list* (string-sha256 e) | |
(file-sha256 e token) | |
accum)] | |
[(eq? type 'dir) (loop e accum)] | |
[else accum])) | |
accum | |
(ls dir))) | |
"") | |
no-sha256)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment