Added makefile.rkt
This commit is contained in:
@@ -1,3 +1,11 @@
|
||||
0.1.5
|
||||
|
||||
Added raco helper for invoking Racket tools from makefile recipes.
|
||||
The helper prefers the raco executable from the current Racket installation,
|
||||
then the directory of the current racket executable, and uses PATH only as a
|
||||
fallback.
|
||||
Added documentation and tests for raco command execution.
|
||||
|
||||
0.1.4
|
||||
|
||||
Replaced files-for-regexp, list-dir-re, list-dir-re-r, and filter-dirs with
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#lang racket
|
||||
|
||||
(require "main.rkt"
|
||||
racket/string)
|
||||
|
||||
(target all
|
||||
(displayln "use (make clean) or (make package) or (make commit/push")
|
||||
)
|
||||
|
||||
(target commit
|
||||
(display "commit message:")
|
||||
(flush-output)
|
||||
(let ((line (read-line)))
|
||||
(set! line (string-trim line))
|
||||
(run '(git add -A))
|
||||
(run (append '(git "commit" -m ) (list line))))
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
`racket-makefile` is a small `#lang` for make-style builds. It keeps ordinary
|
||||
Racket available and only adds targets, dependencies, timestamp based rebuilds,
|
||||
phony targets, external command execution and a few cleanup helpers.
|
||||
phony targets, external command execution, Racket tool execution and a few cleanup helpers.
|
||||
|
||||
A makefile only defines its targets. Loading or running the file does not build
|
||||
anything by itself. Builds are started explicitly with `make`.
|
||||
@@ -40,7 +40,7 @@ raco pkg install
|
||||
For a versioned archive, give the package name explicitly:
|
||||
|
||||
```text
|
||||
raco pkg install --name racket-makefile racket-makefile-0.1.4.zip
|
||||
raco pkg install --name racket-makefile racket-makefile-0.1.5.zip
|
||||
```
|
||||
|
||||
## Running targets
|
||||
@@ -78,6 +78,31 @@ registers the targets. Then use the Interactions window:
|
||||
Bare identifiers in `make` are target names, so no quote is needed. For
|
||||
example, `(make clean)` selects the target named `clean`.
|
||||
|
||||
## Running Racket tools with `raco`
|
||||
|
||||
Use `raco` to invoke a Racket tool from a target without depending on `%PATH%`
|
||||
or `$PATH`:
|
||||
|
||||
```racket
|
||||
(phony setup test)
|
||||
|
||||
(target setup
|
||||
(raco '(setup racket-makefile)))
|
||||
|
||||
(target test
|
||||
(raco '(test -p racket-makefile)))
|
||||
```
|
||||
|
||||
`racket-makefile` first looks for `raco` in the console binary directory of
|
||||
the Racket installation that is currently running the makefile. It then tries
|
||||
the directory containing the current `racket` executable and uses `PATH` only
|
||||
as a final fallback. On Windows this normally finds `raco.exe` next to
|
||||
`racket.exe` even when the Racket directory is not in `%PATH%`.
|
||||
|
||||
The argument rules are the same as for `run`: symbols, strings, paths and
|
||||
numbers are converted to command-line arguments, nested lists are flattened,
|
||||
and `$target`, `$deps` and `$<` are available inside a recipe.
|
||||
|
||||
## Regexp-based cleanup
|
||||
|
||||
For cleanup rules that need more control than globs, `racket-makefile` also
|
||||
@@ -98,7 +123,7 @@ so they can be passed directly to `rm-f` and `rm-rf` with `apply`:
|
||||
(apply rm-rf
|
||||
(list-dirs "." #px"compiled$" #:recursive #t))
|
||||
(apply rm-f
|
||||
(list-files "." #px"(?i:(?:[.]bak|~)$)" #:recursive #t))
|
||||
(list-files "." #px"(?i:([.]bak|~)$)" #:recursive #t))
|
||||
(displayln "done."))
|
||||
```
|
||||
|
||||
@@ -131,7 +156,7 @@ with normal definitions and loops:
|
||||
Dependency expressions may produce lists; they are recursively flattened.
|
||||
|
||||
The language adds `target`, `deps`, `phony`, `default-target`, `make`, `run`,
|
||||
`rm-f`, `rm-rf`, `cleanup`, `list-dir/files`, `list-files`, `list-dirs`,
|
||||
`raco`, `rm-f`, `rm-rf`, `cleanup`, `list-dir/files`, `list-files`, `list-dirs`,
|
||||
`$target`, `$deps` and `$<`. Everything else is ordinary Racket.
|
||||
|
||||
See the installed `racket-makefile` Scribble documentation for the full API.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
(define CFLAGS '(-Wall -O2))
|
||||
|
||||
(default-target all)
|
||||
(phony all clean)
|
||||
(phony all clean setup test)
|
||||
|
||||
(target all
|
||||
(deps "hello"))
|
||||
@@ -28,3 +28,10 @@
|
||||
(rm-f "hello")
|
||||
(rm-rf "compiled")
|
||||
(cleanup "scrbl" '("**.html" "**.js" "**.css")))
|
||||
|
||||
|
||||
(target setup
|
||||
(raco '(setup racket-makefile)))
|
||||
|
||||
(target test
|
||||
(raco '(test -p racket-makefile)))
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
(apply rm-rf
|
||||
(list-dirs "." #px"compiled$" #:recursive #t))
|
||||
(apply rm-f
|
||||
(list-files "." #px"(?i:(?:[.]bak|~)$)" #:recursive #t))
|
||||
(list-files "." #px"(?i:([.]bak|~)$)" #:recursive #t))
|
||||
(displayln "done."))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#lang info
|
||||
|
||||
(define pkg-authors '(hnmdijkema))
|
||||
(define version "0.1.4")
|
||||
(define version "0.1.5")
|
||||
(define license 'MIT)
|
||||
(define collection "racket-makefile")
|
||||
(define pkg-desc
|
||||
|
||||
+62
-7
@@ -3,11 +3,14 @@
|
||||
(require file/glob
|
||||
racket/file
|
||||
racket/list
|
||||
racket/path
|
||||
racket/string
|
||||
racket/system
|
||||
setup/dirs
|
||||
"engine.rkt")
|
||||
|
||||
(provide run
|
||||
raco
|
||||
rm-f
|
||||
rm-rf
|
||||
cleanup
|
||||
@@ -21,32 +24,33 @@
|
||||
(error who "~a is only available while a target recipe is running" description))
|
||||
value)
|
||||
|
||||
(define (command-item->strings value)
|
||||
(define (command-item->strings who value)
|
||||
(cond
|
||||
[(eq? value '$target)
|
||||
(list (recipe-value 'run current-target "$target"))]
|
||||
(list (recipe-value who current-target "$target"))]
|
||||
[(eq? value '$deps)
|
||||
(recipe-value 'run current-dependencies "$deps")]
|
||||
(recipe-value who current-dependencies "$deps")]
|
||||
[(eq? value '$<)
|
||||
(define first (current-first-dependency))
|
||||
(unless first
|
||||
(error 'run "$< is not available: this target has no dependencies"))
|
||||
(error who "$< is not available: this target has no dependencies"))
|
||||
(list first)]
|
||||
[(symbol? value) (list (symbol->string value))]
|
||||
[(path? value) (list (path->string value))]
|
||||
[(string? value) (list value)]
|
||||
[(number? value) (list (number->string value))]
|
||||
[(list? value) (append-map command-item->strings value)]
|
||||
[(list? value) (append-map (lambda (item) (command-item->strings who item)) value)]
|
||||
[else
|
||||
(raise-argument-error
|
||||
'run
|
||||
who
|
||||
"command item (string, symbol, path, number, or list)"
|
||||
value)]))
|
||||
|
||||
(define (run command)
|
||||
(unless (list? command)
|
||||
(raise-argument-error 'run "list?" command))
|
||||
(define arguments (append-map command-item->strings command))
|
||||
(define arguments
|
||||
(append-map (lambda (item) (command-item->strings 'run item)) command))
|
||||
(when (null? arguments)
|
||||
(error 'run "empty command"))
|
||||
(define program (car arguments))
|
||||
@@ -59,6 +63,57 @@
|
||||
(error 'run "command failed: ~a" program))
|
||||
(void))
|
||||
|
||||
|
||||
(define (raco-executable-name)
|
||||
(if (eq? (system-type 'os) 'windows)
|
||||
"raco.exe"
|
||||
"raco"))
|
||||
|
||||
(define (existing-raco-in directory)
|
||||
(and directory
|
||||
(let ([path (build-path directory (raco-executable-name))])
|
||||
(and (file-exists? path) path))))
|
||||
|
||||
(define (find-raco-executable)
|
||||
;; Prefer the console executable directory reported by the current Racket
|
||||
;; installation. This does not depend on PATH and therefore also works for
|
||||
;; typical Windows installations under Program Files.
|
||||
(define console-bin
|
||||
(with-handlers ([exn:fail? (lambda (_) #f)])
|
||||
(find-console-bin-dir)))
|
||||
(define from-console-bin
|
||||
(existing-raco-in console-bin))
|
||||
|
||||
;; A useful fallback for portable/non-standard installations: raco normally
|
||||
;; lives alongside the currently running racket executable.
|
||||
(define exec-file
|
||||
(with-handlers ([exn:fail? (lambda (_) #f)])
|
||||
(find-system-path 'exec-file)))
|
||||
(define from-exec-dir
|
||||
(and exec-file
|
||||
(existing-raco-in (path-only exec-file))))
|
||||
|
||||
;; PATH is intentionally last; it might refer to a different Racket install.
|
||||
(or from-console-bin
|
||||
from-exec-dir
|
||||
(find-executable-path (raco-executable-name))
|
||||
(error 'raco "cannot find raco for the current Racket installation")))
|
||||
|
||||
(define (raco command)
|
||||
(unless (list? command)
|
||||
(raise-argument-error 'raco "list?" command))
|
||||
(define arguments
|
||||
(append-map (lambda (item) (command-item->strings 'raco item)) command))
|
||||
(when (null? arguments)
|
||||
(error 'raco "empty command"))
|
||||
(define executable (find-raco-executable))
|
||||
(printf "> ~a ~a\n"
|
||||
(path->string executable)
|
||||
(string-join arguments " "))
|
||||
(unless (apply system* executable arguments)
|
||||
(error 'raco "command failed: ~a" (car arguments)))
|
||||
(void))
|
||||
|
||||
(define (rm-f . paths)
|
||||
(for ([path (in-list paths)])
|
||||
(case (file-or-directory-type path #f)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
racket/file
|
||||
(only-in racket-makefile
|
||||
target deps phony default-target make
|
||||
run rm-f rm-rf cleanup
|
||||
run raco rm-f rm-rf cleanup
|
||||
list-dir/files list-files list-dirs
|
||||
$target $deps $<)))
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
@defmodulelang[racket-makefile]
|
||||
|
||||
@tt{racket-makefile} is a small make-style language built on Racket. It adds
|
||||
targets, dependencies, timestamp based rebuilding, phony targets, command
|
||||
execution, and a few cleanup helpers. The rest of the language is ordinary
|
||||
targets, dependencies, timestamp based rebuilding, phony targets, command execution, Racket tool execution, and a few cleanup helpers. The rest of the language is ordinary
|
||||
Racket.
|
||||
|
||||
A makefile only defines targets. Loading or running the module does not execute
|
||||
@@ -32,7 +31,7 @@ raco pkg install
|
||||
For a versioned archive, specify the package name explicitly:
|
||||
|
||||
@verbatim{
|
||||
raco pkg install --name racket-makefile racket-makefile-0.1.4.zip
|
||||
raco pkg install --name racket-makefile racket-makefile-0.1.5.zip
|
||||
}
|
||||
|
||||
After installation a makefile can start with:
|
||||
@@ -195,6 +194,36 @@ Ordinary Racket values can be inserted with quasiquote:
|
||||
(run `(,CC ,@CFLAGS -o $target $<)))
|
||||
]
|
||||
|
||||
@section{Running Racket tools}
|
||||
|
||||
@defproc[(raco [command list?]) void?]{
|
||||
Runs a @tt{raco} command using the @tt{raco} executable that belongs to the
|
||||
Racket installation currently running the makefile. The helper first checks
|
||||
the console binary directory reported by the current Racket installation, then
|
||||
the directory containing the current @tt{racket} executable, and only then
|
||||
falls back to @tt{PATH}. This makes the helper useful on Windows installations
|
||||
where @tt{raco.exe} is installed next to @tt{racket.exe} but is not on
|
||||
@tt{%PATH%}.
|
||||
|
||||
The command syntax is the same as for @racket[run], except that the executable
|
||||
is supplied automatically. Symbols, strings, paths and numbers are converted
|
||||
to command-line arguments, nested lists are flattened, and the recipe values
|
||||
@racket['$target], @racket['$deps], and @racket['$<] are supported. A non-zero
|
||||
result raises an error.
|
||||
}
|
||||
|
||||
For example:
|
||||
|
||||
@racketblock[
|
||||
(phony setup test)
|
||||
|
||||
(target setup
|
||||
(raco '(setup racket-makefile)))
|
||||
|
||||
(target test
|
||||
(raco '(test -p racket-makefile)))
|
||||
]
|
||||
|
||||
@section{Cleanup helpers}
|
||||
|
||||
@defproc[(rm-f [path path-string?] ...) void?]{
|
||||
@@ -271,7 +300,7 @@ These helpers make regexp-based cleanup concise. For example:
|
||||
(apply rm-rf
|
||||
(list-dirs "." #px"compiled$" #:recursive #t))
|
||||
(apply rm-f
|
||||
(list-files "." #px"(?i:(?:[.]bak|~)$)" #:recursive #t))
|
||||
(list-files "." #px"(?i:([.]bak|~)$)" #:recursive #t))
|
||||
(displayln "done."))
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
#lang racket
|
||||
|
||||
(require rackunit
|
||||
(only-in racket-makefile raco))
|
||||
|
||||
(check-not-exn
|
||||
(lambda ()
|
||||
(raco '(help))))
|
||||
|
||||
;; It must not require raco to be on PATH. The current Racket installation
|
||||
;; should locate its own raco first.
|
||||
(define env (environment-variables-copy (current-environment-variables)))
|
||||
(environment-variables-set! env #"PATH" #f)
|
||||
(parameterize ([current-environment-variables env])
|
||||
(check-not-exn
|
||||
(lambda ()
|
||||
(raco '(help)))))
|
||||
Reference in New Issue
Block a user