Initial import
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
#lang scribble/manual
|
||||
|
||||
@(require (for-label racket/base
|
||||
racket/file
|
||||
(only-in racket-makefile
|
||||
target deps phony default-target
|
||||
run rm-f rm-rf cleanup
|
||||
$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, and a few cleanup helpers. The rest of the language is ordinary
|
||||
Racket.
|
||||
|
||||
@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.1.zip
|
||||
}
|
||||
|
||||
After installation a makefile can start with:
|
||||
|
||||
@verbatim{
|
||||
#lang racket-makefile
|
||||
}
|
||||
|
||||
Run a makefile with Racket:
|
||||
|
||||
@verbatim{
|
||||
racket Makefile.rkt
|
||||
racket Makefile.rkt clean
|
||||
racket Makefile.rkt clean package
|
||||
}
|
||||
|
||||
With no command-line target, @racket[default-target] is used. If no default is
|
||||
specified, the first declared target is used.
|
||||
|
||||
@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 when no target is supplied on the command line.
|
||||
}
|
||||
|
||||
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{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{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))
|
||||
]
|
||||
|
||||
@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))
|
||||
]
|
||||
Reference in New Issue
Block a user