# 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.