racket-makefile
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, 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.
#lang racket-makefile
(define CC 'cc)
(define CFLAGS '(-Wall -O2))
(default-target all)
(phony all clean)
(target all
(deps "hello"))
(target "hello"
(deps "hello.c")
(run `(,CC ,@CFLAGS -o $target $<)))
(target clean
(rm-f "hello")
(rm-rf "compiled")
(cleanup "scrbl" '("**.html" "**.js" "**.css")))
Installation
Install a local checkout with:
raco pkg install
For a versioned archive, give the package name explicitly:
raco pkg install --name racket-makefile racket-makefile-0.1.5.zip
Running targets
Load the makefile with -t and evaluate a make expression with -e:
racket -t Makefile.rkt -e "(make)"
racket -t Makefile.rkt -e "(make all)"
racket -t Makefile.rkt -e "(make clean)"
racket -t Makefile.rkt -e "(make clean all)"
(make) uses the target selected with default-target. If no default target is
specified, the first declared target is used.
Loading the file alone only registers the targets:
racket -t Makefile.rkt
No recipe is executed in that case.
In DrRacket, open the makefile and press Run. This also only loads and registers the targets. Then use the Interactions window:
> (make)
> (make clean)
> (make all)
> (make clean all)
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:
(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
provides small directory/regexp helpers. The results are ordinary path lists,
so they can be passed directly to rm-f and rm-rf with apply:
#lang racket-makefile
(default-target all)
(phony all clean)
(target all
(displayln "use (make clean)"))
(target clean
(display "cleaning up...")
(apply rm-rf
(list-dirs "." #px"compiled$" #:recursive #t))
(apply rm-f
(list-files "." #px"(?i:([.]bak|~)$)" #:recursive #t))
(displayln "done."))
list-dir/files returns matching files and directories. list-files and
list-dirs restrict the result to files or directories. All three scan one
directory level by default; use #:recursive #t to walk the complete tree.
Non-recursive results are returned as complete paths, so the lists can be fed
directly to rm-f or rm-rf with apply.
Generated targets
Because the rest of the language is ordinary Racket, targets can be generated with normal definitions and loops:
(define sources '("foo.c" "bar.c" "baz.c"))
(define objects
(for/list ([src sources])
(define obj (path-replace-extension src #".o"))
(target obj
(deps src)
(run `(cc -c $< -o $target)))
obj))
(target all
(deps objects))
Dependency expressions may produce lists; they are recursively flattened.
The language adds target, deps, phony, default-target, make, run,
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.