# racket-makefile `racket-makefile` provides make-style dependency builds as ordinary Racket functionality. `racket-makefile` is a Racket library and has no dependency on Rash. Rash integration is provided separately by the `rash-makefile` package. ## Functional makefiles A makefile is ordinary Racket code inside a `makefile` form. The prefix and static symbolic target names are explicit values: ```racket #lang racket (require racket-makefile) (makefile 'wiki (default-target 'all) (phony 'all 'clean 'status) (target 'status (displayln "status")) (target 'clean (rm-rf "compiled")) (target 'all (deps 'status) (displayln "all"))) ``` A statically quoted target name defines a real named procedure. The example above defines, among others: ```racket makefile-target-wiki-status makefile-target-wiki-clean makefile-target-wiki-all ``` They are ordinary procedures: ```racket (procedure? makefile-target-wiki-status) (makefile-target-wiki-status) ``` `make` is also an ordinary procedure: ```racket (make) (make 'status) (make 'clean 'all) ``` `make` resolves target names for the active makefile in an internal prefix-aware registry. It handles dependencies and timestamp checks and invokes the registered target procedure when the target needs to run. ## Ordinary Racket and generated targets The body of `makefile` is not restricted to target clauses. Definitions, loops, conditionals, and other Racket forms can be used directly. A target name is an expression. A quoted target name such as `'all` is known while the module is expanded and therefore gets a named Racket binding. A target name such as `obj` is evaluated while the makefile is registered and can therefore be generated by ordinary Racket code. ```racket (makefile 'stuff (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)) (default-target 'all) (phony 'all 'clean) (target 'all (deps objects)) (target 'clean (apply rm-f objects))) ``` The loop registers `foo.o`, `bar.o`, and `baz.o` as separate target procedures. Each generated procedure closes over the corresponding `src` and `obj` values. The static targets still define `makefile-target-stuff-all` and `makefile-target-stuff-clean` as normal Racket bindings. ## Makefile prefixes Every makefile has an explicit prefix: ```racket (makefile 'wiki (target 'status ...)) (makefile 'audio (target 'status ...)) ``` Targets with the same name can therefore coexist. Evaluating a `makefile` form makes that prefix current. Consequently the last makefile definition evaluated is the one used by an unqualified `make` call: ```racket (current-makefile-prefix) ;; 'audio (make 'status) ;; invokes makefile-target-audio-status through the make engine ``` `current-makefile-prefix` is a parameter, so another registered makefile can be selected explicitly: ```racket (current-makefile-prefix 'wiki) (make 'status) ``` Re-evaluating a makefile with the same prefix replaces the registrations for that prefix. ## Inspecting makefiles and targets The registered makefiles and targets can be inspected without exposing the mutable registry: ```racket (makefile-prefixes) ;; '(wiki audio) (makefile-targets) ;; targets for the current prefix (makefile-targets 'wiki) ;; targets registered for 'wiki (makefile-target-exists? 'status) (makefile-target-exists? 'wiki 'status) (makefile-target-procedure 'status) (makefile-target-procedure 'wiki 'status) ``` `makefile-targets` returns targets in registration order and preserves their original values. A quoted symbolic target is returned as a symbol, while a dynamically generated path target is returned as a path. The forms without an explicit prefix use `current-makefile-prefix`. ## Rash integration Rash integration is intentionally kept out of this package. Install and require the separate `rash-makefile` package when `make target` line syntax is desired. ## Targets and dependencies `target`, `phony`, and `default-target` are valid only in the lexical body of `makefile`. They may occur inside ordinary Racket forms nested in that body. Dependencies are ordinary Racket expressions and may return nested lists; the build engine flattens them. ```racket (makefile 'example (define prerequisites '("one.c" "two.c")) (target 'program (deps prerequisites) (run '(cc -o program one.c two.c)))) ``` The automatic recipe values remain available inside target procedures: ```racket $target $deps $< ``` Because a target is a real procedure, calling a generated static procedure directly runs its recipe directly. Calling it through `make` adds dependency traversal and timestamp-based rebuilding. ## Refreshing A `makefile` form remembers the source module in which it occurs. `refresh-makefile` reloads that source without restarting the Racket process: ```racket (refresh-makefile) (make 'all) ``` In DrRacket, pressing **Run** is normally the simpler way to reevaluate a Rash or Racket makefile. ## Helpers The functional helpers provided by `racket-makefile` are `run`, `raco`, `rm-f`, `rm-rf`, `cleanup`, `list-dir/files`, `list-files`, and `list-dirs`. `raco` locates the executable belonging to the active Racket installation before falling back to `PATH`. `racket-makefile` also continues to re-export the package APIs it uses for build scripts, including `git-cli`, `package-zipper`, `net/sendurl`, and `racket/string`.