177 lines
4.9 KiB
Markdown
177 lines
4.9 KiB
Markdown
# 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`.
|
|
|
|
```racket
|
|
#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:
|
|
|
|
```text
|
|
raco pkg install
|
|
```
|
|
|
|
For a versioned archive, give the package name explicitly:
|
|
|
|
```text
|
|
raco pkg install --name racket-makefile racket-makefile-0.1.8.zip
|
|
```
|
|
|
|
## Running targets
|
|
|
|
Load the makefile with `-t` and evaluate a `make` expression with `-e`:
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```racket
|
|
> (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`.
|
|
|
|
After editing the makefile, use `refresh-makefile` in the same Interactions
|
|
window to reload the makefile without restarting the Racket process:
|
|
|
|
```racket
|
|
> (refresh-makefile)
|
|
> (make all)
|
|
```
|
|
|
|
This works both with `#lang racket-makefile` and with an ordinary `#lang racket`
|
|
makefile that requires `main.rkt`. It is useful when the makefile requires modules
|
|
that are expensive to load. Unchanged required modules can remain instantiated
|
|
while the target definitions from the makefile are registered again.
|
|
|
|
## 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
|
|
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`:
|
|
|
|
```racket
|
|
#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. The regexp is matched
|
|
against the file or directory name itself, not against the complete path.
|
|
`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. Results are returned as usable 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:
|
|
|
|
```racket
|
|
(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.
|