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 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.4.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.
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,
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.