2026-08-14 11:27:26 +02:00
2026-08-09 00:32:36 +02:00
2026-08-08 14:32:18 +02:00
2026-08-09 14:56:42 +02:00
oke
2026-08-13 18:42:08 +02:00
oke
2026-08-13 18:42:08 +02:00
oke
2026-08-13 18:42:08 +02:00
2026-08-08 13:43:39 +02:00
2026-08-14 11:27:26 +02:00
2026-08-14 11:27:26 +02:00

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

After editing the makefile, use refresh-makefile in the same Interactions window to reload the makefile without restarting the Racket process:

> (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:

(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. 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:

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

S
Description
No description provided
Readme MIT 113 KiB
Languages
Racket 96.2%
Makefile 3.8%