Files
racket-makefile/scrbl/racket-makefile.scrbl
T

177 lines
7.6 KiB
Racket

#lang scribble/manual
@(require (for-label racket
racket/file
racket-makefile))
@title{racket-makefile}
@author["Hans Dijkema / hans@dijkewijk.nl"]
@defmodule[racket-makefile]
@tt{racket-makefile} provides make-style dependency builds as ordinary Racket functionality. The package has no Rash dependency. Rash integration is provided by the separate @tt{rash-makefile} package.
@section{A functional makefile}
A makefile is declared with one prefix and a set of target clauses:
@racketblock[
(require racket-makefile)
(makefile wiki
(default-target all)
(phony status clean all)
(target status
(displayln "status"))
(target clean
(rm-rf "compiled"))
(target all
(deps status)
(displayln "all")))
]
The @racket[makefile] form defines real Racket procedures. The example defines @racket[makefile-target-wiki-status], @racket[makefile-target-wiki-clean], and @racket[makefile-target-wiki-all]. They can be inspected or called like any other procedure.
@racketblock[
(procedure? makefile-target-wiki-status)
(makefile-target-wiki-status)
]
Calling a generated target procedure directly executes the recipe directly. Calling the target through @racket[make] adds dependency traversal and timestamp based rebuilding.
@section{Makefile definitions}
@defform[(makefile prefix clause ...)]{
Defines one prefixed makefile. The @racket[prefix] is a literal identifier, string, path, or quoted symbol. Each @racket[clause] is a @racket[target], @racket[phony], or @racket[default-target] clause.
Before registering the new clauses, registrations for the same prefix are removed. After all clauses are registered, @racket[current-makefile-prefix] is set to the prefix. Consequently the last evaluated @racket[makefile] form becomes the active makefile.
A target named @racket[status] in a makefile with prefix @racket[wiki] defines the procedure @racket[makefile-target-wiki-status].
}
@defform[(target name (deps dependency ...) body ...)]{
Defines a named target procedure inside @racket[makefile]. The @racket[name] is a literal identifier, string, path, or quoted symbol. The target procedure executes @racket[body ...].
Dependency expressions are evaluated when the makefile is registered. A dependency expression may produce nested lists; the build engine flattens them.
}
A target without a @racket[deps] clause has no dependencies.
@defform[(deps dependency ...)]{
Specifies target dependencies. The form is valid only as the dependency clause of @racket[target].
}
@defform[(phony name ...)]{
Marks the named targets as phony for the surrounding makefile prefix. A phony target is always executed when requested or reached as a dependency.
}
@defform[(default-target name)]{
Selects the target used by @racket[(make)] for the surrounding makefile prefix. If no default is specified, the first declared target is used.
}
The declaration forms @racket[target], @racket[deps], @racket[phony], and @racket[default-target] are not standalone declarations in version 0.3.0. They are clauses of @racket[makefile].
@section{Executing targets}
@defproc[(make [name (or/c symbol? path-string?)] ...) void?]{
Builds the supplied targets in the makefile selected by @racket[current-makefile-prefix]. @racket[make] is an ordinary procedure, so symbolic target names are quoted explicitly.
With no arguments, the configured default target is used. If no explicit default exists, the first target of the active makefile is used. Multiple supplied targets are processed in order, and shared dependencies are built once during one @racket[make] call.
For a target that needs rebuilding, the engine looks up the target procedure in @racket[makefile-targets] and calls it.
}
@defthing[current-makefile-prefix parameter?]{
A parameter containing the active makefile prefix, or @racket[#f] before any makefile has been evaluated. Evaluating @racket[makefile] sets the parameter persistently to that makefile's prefix.
Another registered makefile can be selected explicitly:
@racketblock[
(current-makefile-prefix 'wiki)
(make 'status)
]
}
@defthing[makefile-targets hash?]{
The global prefix-aware target registry. Registry values are the generated target procedures themselves. This binding is exposed mainly for inspection and tooling; normal target execution should use @racket[make].
}
@section{Rash integration}
Rash integration is intentionally provided by the separate @tt{rash-makefile} package so that @tt{racket-makefile} remains a pure Racket dependency.
@section{Recipe context}
@defidform[$target]{The current target name while a generated target procedure is running.}
@defidform[$deps]{The flattened list of dependencies of the current target.}
@defidform[$<]{The first dependency of the current target. An error is raised when the target has no dependencies.}
The generated procedure establishes this context even when called directly.
@section{Dependency processing}
A non-phony target is rebuilt when its output does not exist or when a dependency is newer than the target. Registered target dependencies are built first. A dependency that is neither a registered target in the same prefix nor an existing file is an error. Dependency cycles are reported as errors.
Multiple makefile prefixes can remain registered simultaneously. Dependency lookup stays within the prefix of the target being built.
@section{Refreshing definitions}
@defproc[(refresh-makefile) void?]{
Reloads the source module containing the most recently evaluated @racket[makefile] form. Existing makefile registrations are cleared before the source is evaluated again. Modules already instantiated in the current namespace are reused.
In DrRacket, pressing @bold{Run} is usually the simpler way to reevaluate a Racket or Rash makefile.
}
@section{Running commands}
@defproc[(run [command list?]) void?]{
Runs an external command directly without an intermediate shell. Symbols, strings, paths, numbers, and nested lists are converted to command-line arguments. A non-zero result raises an error.
The symbols @racket['$target], @racket['$deps], and @racket['$<] are expanded from the current target context when they occur in the command list.
}
@defproc[(raco [command list?]) void?]{
Runs @tt{raco} from the active Racket installation. The command is supplied as a list, for example @racket[(raco '(setup racket-makefile))]. The helper first looks in the console executable directory of the current Racket installation, then next to the running Racket executable, and only then on @tt{PATH}. A non-zero result raises an error.
}
@section{Cleanup helpers}
@defproc[(rm-f [path path-string?] ...) void?]{
Removes files when they exist. Missing files are ignored. Directories are rejected.
}
@defproc[(rm-rf [path path-string?] ...) void?]{
Removes files or directory trees recursively. Missing paths are ignored.
}
@defproc[(cleanup [directory path-string?] [patterns list?]) void?]{
Removes files below @racket[directory] that match the supplied Racket glob patterns.
}
@defproc[(list-dir/files [directory path-string?]
[regexp regexp?]
[#:recursive recursive any/c #f])
list?]{
Returns matching files and directories. The regular expression is applied to each file or directory name, not to the complete path.
}
@defproc[(list-files [directory path-string?]
[regexp regexp?]
[#:recursive recursive any/c #f])
list?]{
Like @racket[list-dir/files], but keeps only files.
}
@defproc[(list-dirs [directory path-string?]
[regexp regexp?]
[#:recursive recursive any/c #f])
list?]{
Like @racket[list-dir/files], but keeps only directories.
}