Removed rash integration again and backported raco call.
This commit is contained in:
@@ -1,375 +0,0 @@
|
||||
#lang scribble/manual
|
||||
|
||||
@(require (for-label racket/base
|
||||
racket/file
|
||||
(only-in racket-makefile
|
||||
target deps phony default-target make refresh-makefile
|
||||
run raco rm-f rm-rf cleanup
|
||||
list-dir/files list-files list-dirs
|
||||
$target $deps $<)))
|
||||
|
||||
@title{racket-makefile}
|
||||
@author{Hans Dijkema}
|
||||
|
||||
@defmodulelang[racket-makefile]
|
||||
|
||||
@tt{racket-makefile} is a small make-style language built on Racket. It adds
|
||||
targets, dependencies, timestamp based rebuilding, phony targets, command execution, Racket tool execution, and a few cleanup helpers. The rest of the language is ordinary
|
||||
Racket.
|
||||
|
||||
A makefile only defines targets. Loading or running the module does not execute
|
||||
any recipe automatically. Builds are started explicitly with @racket[make].
|
||||
|
||||
@section{Installation}
|
||||
|
||||
Install the package from a local checkout with:
|
||||
|
||||
@verbatim{
|
||||
raco pkg install
|
||||
}
|
||||
|
||||
For a versioned archive, specify the package name explicitly:
|
||||
|
||||
@verbatim{
|
||||
raco pkg install --name racket-makefile racket-makefile-0.1.8.zip
|
||||
}
|
||||
|
||||
After installation a makefile can start with:
|
||||
|
||||
@verbatim{
|
||||
#lang racket-makefile
|
||||
}
|
||||
|
||||
@section{A first makefile}
|
||||
|
||||
For example:
|
||||
|
||||
@racketblock[
|
||||
(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")))
|
||||
]
|
||||
|
||||
Loading the file only registers these targets; it does not build @tt{all} or
|
||||
any other target.
|
||||
|
||||
@section{Executing targets}
|
||||
|
||||
@defform[(make name ...)]{
|
||||
Builds the supplied targets. A bare identifier is always interpreted as a
|
||||
literal target name, so @racket[(make clean)] selects the target named
|
||||
@racket['clean] and does not require quoting.
|
||||
|
||||
With no arguments, @racket[(make)] builds the target selected by
|
||||
@racket[default-target]. If no default target has been specified, the first
|
||||
declared target is used.
|
||||
|
||||
Multiple targets are allowed and are processed in the supplied order, for
|
||||
example @racket[(make clean all)]. Dependencies that are shared by multiple
|
||||
selected targets are built only once during that @racket[make] invocation.
|
||||
}
|
||||
|
||||
From the command line, load the makefile with @tt{-t} and evaluate a
|
||||
@racket[make] form with @tt{-e}:
|
||||
|
||||
@verbatim{
|
||||
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)"
|
||||
}
|
||||
|
||||
Loading a makefile without @tt{-e} executes no target:
|
||||
|
||||
@verbatim{
|
||||
racket -t Makefile.rkt
|
||||
}
|
||||
|
||||
In DrRacket, open the makefile and press @bold{Run}. This loads and registers
|
||||
the targets. Then execute targets in the Interactions window:
|
||||
|
||||
@verbatim{
|
||||
> (make)
|
||||
> (make clean)
|
||||
> (make all)
|
||||
> (make clean all)
|
||||
}
|
||||
|
||||
After editing the makefile, reload its target definitions in the same Racket
|
||||
process with:
|
||||
|
||||
@verbatim{
|
||||
> (refresh-makefile)
|
||||
> (make all)
|
||||
}
|
||||
|
||||
@defproc[(refresh-makefile) void?]{
|
||||
Reloads the most recently loaded @tt{racket-makefile} source file and registers
|
||||
its targets again without restarting the Racket process. This works for both
|
||||
@tt{#lang racket-makefile} and ordinary @tt{#lang racket} makefiles that require
|
||||
@tt{main.rkt}. Modules required by the makefile that are already instantiated
|
||||
are reused. This is useful for makefiles that require expensive libraries.
|
||||
}
|
||||
|
||||
@section{Targets and dependencies}
|
||||
|
||||
@defform[(target name (deps dependency ...) body ...)]{
|
||||
Defines a target. @racket[name] can be a symbol, string, path, or a Racket
|
||||
expression producing one of those values. Each @racket[dependency] can also
|
||||
produce a list; dependency lists are recursively flattened.
|
||||
|
||||
An unbound identifier is treated as a literal symbol. Consequently,
|
||||
@racket[(target clean ...)] defines the target @racket['clean], while a bound
|
||||
identifier can be used to generate targets from ordinary Racket code. If a
|
||||
literal target name happens to be bound by Racket, quote it explicitly, for
|
||||
example @racket[(target 'compile ...)].
|
||||
|
||||
The body is not evaluated when the target is declared. It is saved as the
|
||||
target recipe and evaluated only when the target must be rebuilt.
|
||||
}
|
||||
|
||||
@defform[(deps dependency ...)]{
|
||||
Specifies the dependencies of a target. Dependency expressions that produce
|
||||
lists are automatically spliced into the dependency list. @racket[deps] is only
|
||||
valid directly inside @racket[target].
|
||||
}
|
||||
|
||||
@defform[(phony name ...)]{
|
||||
Marks targets as phony. A phony target is always executed when requested or
|
||||
when reached as a dependency.
|
||||
}
|
||||
|
||||
@defform[(default-target name)]{
|
||||
Selects the target used by @racket[(make)] when no target is supplied. If no
|
||||
default target is specified, the first declared target is used.
|
||||
}
|
||||
|
||||
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 nor an existing file
|
||||
is an error. Dependency cycles are reported as errors.
|
||||
|
||||
@section{Recipe context}
|
||||
|
||||
Inside a target recipe the following identifiers are available:
|
||||
|
||||
@defidform[$target]{The current target name.}
|
||||
|
||||
@defidform[$deps]{A list containing all dependencies of the current target.}
|
||||
|
||||
@defidform[$<]{The first dependency of the current target. An error is raised
|
||||
when the target has no dependencies.}
|
||||
|
||||
The same names can occur as symbols inside a quoted command passed to
|
||||
@racket[run].
|
||||
|
||||
@section{Running commands}
|
||||
|
||||
@defproc[(run [command list?]) void?]{
|
||||
Runs an external command directly, without an intermediate shell. The first
|
||||
item is the executable and the remaining items are arguments. Symbols are
|
||||
converted to strings. Nested lists are flattened, which makes Racket lists of
|
||||
flags convenient to use.
|
||||
|
||||
The symbols @racket['$target], @racket['$deps], and @racket['$<] are expanded
|
||||
from the current recipe. @racket['$deps] is spliced into the command.
|
||||
|
||||
A non-zero command result raises an error.
|
||||
}
|
||||
|
||||
For example:
|
||||
|
||||
@racketblock[
|
||||
(target "hello"
|
||||
(deps "hello.c")
|
||||
(run '(cc -Wall -O2 -o $target $<)))
|
||||
]
|
||||
|
||||
Ordinary Racket values can be inserted with quasiquote:
|
||||
|
||||
@racketblock[
|
||||
(define CC 'cc)
|
||||
(define CFLAGS '(-Wall -O2))
|
||||
|
||||
(target "hello"
|
||||
(deps "hello.c")
|
||||
(run `(,CC ,@CFLAGS -o $target $<)))
|
||||
]
|
||||
|
||||
@section{Running Racket tools}
|
||||
|
||||
@defproc[(raco [command list?]) void?]{
|
||||
Runs a @tt{raco} command using the @tt{raco} executable that belongs to the
|
||||
Racket installation currently running the makefile. The helper first checks
|
||||
the console binary directory reported by the current Racket installation, then
|
||||
the directory containing the current @tt{racket} executable, and only then
|
||||
falls back to @tt{PATH}. This makes the helper useful on Windows installations
|
||||
where @tt{raco.exe} is installed next to @tt{racket.exe} but is not on
|
||||
@tt{%PATH%}.
|
||||
|
||||
The command syntax is the same as for @racket[run], except that the executable
|
||||
is supplied automatically. Symbols, strings, paths and numbers are converted
|
||||
to command-line arguments, nested lists are flattened, and the recipe values
|
||||
@racket['$target], @racket['$deps], and @racket['$<] are supported. A non-zero
|
||||
result raises an error.
|
||||
}
|
||||
|
||||
For example:
|
||||
|
||||
@racketblock[
|
||||
(phony setup test)
|
||||
|
||||
(target setup
|
||||
(raco '(setup racket-makefile)))
|
||||
|
||||
(target test
|
||||
(raco '(test -p racket-makefile)))
|
||||
]
|
||||
|
||||
@section{Cleanup helpers}
|
||||
|
||||
@defproc[(rm-f [path path-string?] ...) void?]{
|
||||
Removes files when they exist. Missing files are ignored. Directories are not
|
||||
removed; use @racket[rm-rf] for those.
|
||||
}
|
||||
|
||||
@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. Directories themselves are left in place. A pattern containing
|
||||
@tt{**} searches recursively.
|
||||
}
|
||||
|
||||
For example:
|
||||
|
||||
@racketblock[
|
||||
(target clean
|
||||
(rm-rf "compiled")
|
||||
(cleanup "scrbl"
|
||||
'("**.html"
|
||||
"**.js"
|
||||
"**.css")))
|
||||
]
|
||||
|
||||
@section{Regexp directory helpers}
|
||||
|
||||
@defproc[(list-dir/files [directory path-string?]
|
||||
[regexp regexp?]
|
||||
[#:recursive recursive any/c #f])
|
||||
list?]{
|
||||
Returns entries below @racket[directory] whose file or directory name matches
|
||||
@racket[regexp]. The regexp is applied to @racket[(file-name-from-path path)],
|
||||
not to the complete path. By default only the direct contents of
|
||||
@racket[directory] are inspected. With @racket[#:recursive #t], the complete
|
||||
tree is walked using @racket[in-directory], and both files and directories can
|
||||
be returned.
|
||||
|
||||
For a non-recursive listing, complete paths are built from @racket[directory],
|
||||
so the result can be passed directly to file operations such as
|
||||
@racket[rm-f] and @racket[rm-rf].
|
||||
}
|
||||
|
||||
@defproc[(list-files [directory path-string?]
|
||||
[regexp regexp?]
|
||||
[#:recursive recursive any/c #f])
|
||||
list?]{
|
||||
Like @racket[list-dir/files], but keeps only paths for which
|
||||
@racket[file-exists?] is true.
|
||||
}
|
||||
|
||||
@defproc[(list-dirs [directory path-string?]
|
||||
[regexp regexp?]
|
||||
[#:recursive recursive any/c #f])
|
||||
list?]{
|
||||
Like @racket[list-dir/files], but keeps only paths for which
|
||||
@racket[directory-exists?] is true.
|
||||
}
|
||||
|
||||
These helpers make regexp-based cleanup concise. For example:
|
||||
|
||||
@racketblock[
|
||||
(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."))
|
||||
]
|
||||
|
||||
The second regular expression is case-insensitive and matches names ending in
|
||||
@tt{.bak} or @tt{~}.
|
||||
|
||||
@section{Using ordinary Racket}
|
||||
|
||||
No separate make programming language is introduced. Definitions, functions,
|
||||
loops, conditionals, modules, and Racket libraries remain available. For
|
||||
example, targets can be generated in a loop:
|
||||
|
||||
@racketblock[
|
||||
(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))
|
||||
]
|
||||
|
||||
The expression @racket[objects] evaluates to a list of dependencies. Dependency
|
||||
lists are recursively flattened by @tt{racket-makefile}.
|
||||
|
||||
@section{Package example}
|
||||
|
||||
The optional @hyperlink["https://docs.racket-lang.org/package-zipper/index.html"]{@tt{package-zipper}}
|
||||
package combines naturally with @tt{racket-makefile}:
|
||||
|
||||
@racketblock[
|
||||
(require package-zipper)
|
||||
|
||||
(default-target package)
|
||||
(phony clean package)
|
||||
|
||||
(target clean
|
||||
(rm-rf "compiled")
|
||||
(cleanup "scrbl" '("**.html" "**.js" "**.css")))
|
||||
|
||||
(target package
|
||||
(zip-package))
|
||||
]
|
||||
|
||||
The package target can then be executed explicitly, for example:
|
||||
|
||||
@verbatim{
|
||||
racket -t Makefile.rkt -e "(make package)"
|
||||
}
|
||||
+94
-294
@@ -1,376 +1,176 @@
|
||||
#lang scribble/manual
|
||||
|
||||
@(require (for-label racket/base
|
||||
@(require (for-label racket
|
||||
racket/file
|
||||
(only-in racket-makefile
|
||||
target deps phony default-target make refresh-makefile
|
||||
run raco rm-f rm-rf cleanup
|
||||
list-dir/files list-files list-dirs
|
||||
$target $deps $<)))
|
||||
racket-makefile))
|
||||
|
||||
@title{racket-makefile}
|
||||
@author{Hans Dijkema}
|
||||
@author["Hans Dijkema / hans@dijkewijk.nl"]
|
||||
|
||||
@defmodulelang[racket-makefile]
|
||||
@defmodule[racket-makefile]
|
||||
|
||||
@tt{racket-makefile} is a small make-style language built on Racket. It adds
|
||||
targets, dependencies, timestamp based rebuilding, phony targets, command execution, Racket tool execution, and a few cleanup helpers. The rest of the language is ordinary
|
||||
Racket.
|
||||
@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.
|
||||
|
||||
A makefile only defines targets. Loading or running the module does not execute
|
||||
any recipe automatically. Builds are started explicitly with @racket[make].
|
||||
@section{A functional makefile}
|
||||
|
||||
@section{Installation}
|
||||
|
||||
@tt{racket-makefile} is available through the
|
||||
@hyperlink["https://pkgs.racket-lang.org/"]{Racket Package Catalog}.
|
||||
|
||||
Install it with:
|
||||
|
||||
@verbatim{
|
||||
raco pkg install racket-makefile
|
||||
}
|
||||
|
||||
or using the @tt{DrRacket} package manager.
|
||||
|
||||
After installation a makefile can start with:
|
||||
|
||||
@verbatim{
|
||||
#lang racket-makefile
|
||||
}
|
||||
|
||||
If you want to see some example of a @tt{Makefile.rkt}}, you can look in the module code of @tt{racket-makefile}.
|
||||
|
||||
@section{A first makefile}
|
||||
|
||||
For example:
|
||||
A makefile is declared with one prefix and a set of target clauses:
|
||||
|
||||
@racketblock[
|
||||
(define CC 'cc)
|
||||
(define CFLAGS '(-Wall -O2))
|
||||
(require racket-makefile)
|
||||
|
||||
(default-target all)
|
||||
(phony all clean)
|
||||
(makefile wiki
|
||||
(default-target all)
|
||||
(phony status clean all)
|
||||
|
||||
(target all
|
||||
(deps "hello"))
|
||||
(target status
|
||||
(displayln "status"))
|
||||
|
||||
(target "hello"
|
||||
(deps "hello.c")
|
||||
(run `(,CC ,@CFLAGS -o $target $<)))
|
||||
(target clean
|
||||
(rm-rf "compiled"))
|
||||
|
||||
(target clean
|
||||
(rm-f "hello")
|
||||
(rm-rf "compiled")
|
||||
(cleanup "scrbl" '("**.html" "**.js" "**.css")))
|
||||
(target all
|
||||
(deps status)
|
||||
(displayln "all")))
|
||||
]
|
||||
|
||||
Loading the file only registers these targets; it does not build @tt{all} or
|
||||
any other target.
|
||||
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.
|
||||
|
||||
@section{Executing targets}
|
||||
@racketblock[
|
||||
(procedure? makefile-target-wiki-status)
|
||||
(makefile-target-wiki-status)
|
||||
]
|
||||
|
||||
@defform[(make name ...)]{
|
||||
Builds the supplied targets. A bare identifier is always interpreted as a
|
||||
literal target name, so @racket[(make clean)] selects the target named
|
||||
@racket['clean] and does not require quoting.
|
||||
Calling a generated target procedure directly executes the recipe directly. Calling the target through @racket[make] adds dependency traversal and timestamp based rebuilding.
|
||||
|
||||
With no arguments, @racket[(make)] builds the target selected by
|
||||
@racket[default-target]. If no default target has been specified, the first
|
||||
declared target is used.
|
||||
@section{Makefile definitions}
|
||||
|
||||
Multiple targets are allowed and are processed in the supplied order, for
|
||||
example @racket[(make clean all)]. Dependencies that are shared by multiple
|
||||
selected targets are built only once during that @racket[make] invocation.
|
||||
@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].
|
||||
}
|
||||
|
||||
From the command line, load the makefile with @tt{-t} and evaluate a
|
||||
@racket[make] form with @tt{-e}:
|
||||
|
||||
@verbatim{
|
||||
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)"
|
||||
}
|
||||
|
||||
Loading a makefile without @tt{-e} executes no target:
|
||||
|
||||
@verbatim{
|
||||
racket -t Makefile.rkt
|
||||
}
|
||||
|
||||
In DrRacket, open the makefile and press @bold{Run}. This loads and registers
|
||||
the targets. Then execute targets in the Interactions window:
|
||||
|
||||
@verbatim{
|
||||
> (make)
|
||||
> (make clean)
|
||||
> (make all)
|
||||
> (make clean all)
|
||||
}
|
||||
|
||||
After editing the makefile, reload its target definitions in the same Racket
|
||||
process with:
|
||||
|
||||
@verbatim{
|
||||
> (refresh-makefile)
|
||||
> (make all)
|
||||
}
|
||||
|
||||
@defproc[(refresh-makefile) void?]{
|
||||
Reloads the most recently loaded @tt{racket-makefile} source file and registers
|
||||
its targets again without restarting the Racket process. This works for both
|
||||
@tt{#lang racket-makefile} and ordinary @tt{#lang racket} makefiles that require
|
||||
@tt{main.rkt}. Modules required by the makefile that are already instantiated
|
||||
are reused. This is useful for makefiles that require expensive libraries.
|
||||
}
|
||||
|
||||
@section{Targets and dependencies}
|
||||
|
||||
@defform[(target name (deps dependency ...) body ...)]{
|
||||
Defines a target. @racket[name] can be a symbol, string, path, or a Racket
|
||||
expression producing one of those values. Each @racket[dependency] can also
|
||||
produce a list; dependency lists are recursively flattened.
|
||||
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 ...].
|
||||
|
||||
An unbound identifier is treated as a literal symbol. Consequently,
|
||||
@racket[(target clean ...)] defines the target @racket['clean], while a bound
|
||||
identifier can be used to generate targets from ordinary Racket code. If a
|
||||
literal target name happens to be bound by Racket, quote it explicitly, for
|
||||
example @racket[(target 'compile ...)].
|
||||
|
||||
The body is not evaluated when the target is declared. It is saved as the
|
||||
target recipe and evaluated only when the target must be rebuilt.
|
||||
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 the dependencies of a target. Dependency expressions that produce
|
||||
lists are automatically spliced into the dependency list. @racket[deps] is only
|
||||
valid directly inside @racket[target].
|
||||
Specifies target dependencies. The form is valid only as the dependency clause of @racket[target].
|
||||
}
|
||||
|
||||
@defform[(phony name ...)]{
|
||||
Marks targets as phony. A phony target is always executed when requested or
|
||||
when reached as a dependency.
|
||||
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)] when no target is supplied. If no
|
||||
default target is specified, the first declared target is used.
|
||||
Selects the target used by @racket[(make)] for the surrounding makefile prefix. If no default is specified, the first declared target is used.
|
||||
}
|
||||
|
||||
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 nor an existing file
|
||||
is an error. Dependency cycles are reported as errors.
|
||||
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}
|
||||
|
||||
Inside a target recipe the following identifiers are available:
|
||||
@defidform[$target]{The current target name while a generated target procedure is running.}
|
||||
|
||||
@defidform[$target]{The current target name.}
|
||||
@defidform[$deps]{The flattened list of dependencies of the current target.}
|
||||
|
||||
@defidform[$deps]{A list containing all dependencies of the current target.}
|
||||
@defidform[$<]{The first dependency of the current target. An error is raised when the target has no dependencies.}
|
||||
|
||||
@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.
|
||||
|
||||
The same names can occur as symbols inside a quoted command passed to
|
||||
@racket[run].
|
||||
@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. The first
|
||||
item is the executable and the remaining items are arguments. Symbols are
|
||||
converted to strings. Nested lists are flattened, which makes Racket lists of
|
||||
flags convenient to use.
|
||||
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 recipe. @racket['$deps] is spliced into the command.
|
||||
|
||||
A non-zero command 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.
|
||||
}
|
||||
|
||||
For example:
|
||||
|
||||
@racketblock[
|
||||
(target "hello"
|
||||
(deps "hello.c")
|
||||
(run '(cc -Wall -O2 -o $target $<)))
|
||||
]
|
||||
|
||||
Ordinary Racket values can be inserted with quasiquote:
|
||||
|
||||
@racketblock[
|
||||
(define CC 'cc)
|
||||
(define CFLAGS '(-Wall -O2))
|
||||
|
||||
(target "hello"
|
||||
(deps "hello.c")
|
||||
(run `(,CC ,@CFLAGS -o $target $<)))
|
||||
]
|
||||
|
||||
@section{Running Racket tools}
|
||||
|
||||
@defproc[(raco [command list?]) void?]{
|
||||
Runs a @tt{raco} command using the @tt{raco} executable that belongs to the
|
||||
Racket installation currently running the makefile. The helper first checks
|
||||
the console binary directory reported by the current Racket installation, then
|
||||
the directory containing the current @tt{racket} executable, and only then
|
||||
falls back to @tt{PATH}. This makes the helper useful on Windows installations
|
||||
where @tt{raco.exe} is installed next to @tt{racket.exe} but is not on
|
||||
@tt{%PATH%}.
|
||||
|
||||
The command syntax is the same as for @racket[run], except that the executable
|
||||
is supplied automatically. Symbols, strings, paths and numbers are converted
|
||||
to command-line arguments, nested lists are flattened, and the recipe values
|
||||
@racket['$target], @racket['$deps], and @racket['$<] are supported. A non-zero
|
||||
result raises an error.
|
||||
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.
|
||||
}
|
||||
|
||||
For example:
|
||||
|
||||
@racketblock[
|
||||
(phony setup test)
|
||||
|
||||
(target setup
|
||||
(raco '(setup racket-makefile)))
|
||||
|
||||
(target test
|
||||
(raco '(test -p racket-makefile)))
|
||||
]
|
||||
|
||||
@section{Cleanup helpers}
|
||||
|
||||
@defproc[(rm-f [path path-string?] ...) void?]{
|
||||
Removes files when they exist. Missing files are ignored. Directories are not
|
||||
removed; use @racket[rm-rf] for those.
|
||||
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. Directories themselves are left in place. A pattern containing
|
||||
@tt{**} searches recursively.
|
||||
@defproc[(cleanup [directory path-string?] [patterns list?]) void?]{
|
||||
Removes files below @racket[directory] that match the supplied Racket glob patterns.
|
||||
}
|
||||
|
||||
For example:
|
||||
|
||||
@racketblock[
|
||||
(target clean
|
||||
(rm-rf "compiled")
|
||||
(cleanup "scrbl"
|
||||
'("**.html"
|
||||
"**.js"
|
||||
"**.css")))
|
||||
]
|
||||
|
||||
@section{Regexp directory helpers}
|
||||
|
||||
@defproc[(list-dir/files [directory path-string?]
|
||||
[regexp regexp?]
|
||||
[#:recursive recursive any/c #f])
|
||||
list?]{
|
||||
Returns entries below @racket[directory] whose file or directory name matches
|
||||
@racket[regexp]. The regexp is applied to @racket[(file-name-from-path path)],
|
||||
not to the complete path. By default only the direct contents of
|
||||
@racket[directory] are inspected. With @racket[#:recursive #t], the complete
|
||||
tree is walked using @racket[in-directory], and both files and directories can
|
||||
be returned.
|
||||
|
||||
For a non-recursive listing, complete paths are built from @racket[directory],
|
||||
so the result can be passed directly to file operations such as
|
||||
@racket[rm-f] and @racket[rm-rf].
|
||||
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 paths for which
|
||||
@racket[file-exists?] is true.
|
||||
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 paths for which
|
||||
@racket[directory-exists?] is true.
|
||||
}
|
||||
|
||||
These helpers make regexp-based cleanup concise. For example:
|
||||
|
||||
@racketblock[
|
||||
(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."))
|
||||
]
|
||||
|
||||
The second regular expression is case-insensitive and matches names ending in
|
||||
@tt{.bak} or @tt{~}.
|
||||
|
||||
@section{Using ordinary Racket}
|
||||
|
||||
No separate make programming language is introduced. Definitions, functions,
|
||||
loops, conditionals, modules, and Racket libraries remain available. For
|
||||
example, targets can be generated in a loop:
|
||||
|
||||
@racketblock[
|
||||
(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))
|
||||
]
|
||||
|
||||
The expression @racket[objects] evaluates to a list of dependencies. Dependency
|
||||
lists are recursively flattened by @tt{racket-makefile}.
|
||||
|
||||
@section{Package example}
|
||||
|
||||
The optional @hyperlink["https://docs.racket-lang.org/package-zipper/index.html"]{@tt{package-zipper}}
|
||||
package combines naturally with @tt{racket-makefile}:
|
||||
|
||||
@racketblock[
|
||||
(require package-zipper)
|
||||
|
||||
(default-target package)
|
||||
(phony clean package)
|
||||
|
||||
(target clean
|
||||
(rm-rf "compiled")
|
||||
(cleanup "scrbl" '("**.html" "**.js" "**.css")))
|
||||
|
||||
(target package
|
||||
(zip-package))
|
||||
]
|
||||
|
||||
The package target can then be executed explicitly, for example:
|
||||
|
||||
@verbatim{
|
||||
racket -t Makefile.rkt -e "(make package)"
|
||||
Like @racket[list-dir/files], but keeps only directories.
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user