makefile-targets and some other meta info functions added
This commit is contained in:
@@ -1,3 +1,16 @@
|
|||||||
|
0.3.8
|
||||||
|
- Add makefile-prefixes for inspecting registered makefiles.
|
||||||
|
- Make makefile-targets a read-only query procedure instead of exposing the mutable target registry.
|
||||||
|
- Add makefile-target-exists? and makefile-target-procedure for target inspection.
|
||||||
|
- Preserve the original symbol, string, or path values returned by the inspection API.
|
||||||
|
|
||||||
|
0.3.7
|
||||||
|
- Make the body of makefile ordinary Racket code instead of a fixed clause list.
|
||||||
|
- Require an explicit makefile prefix value, for example (makefile 'stuff ...).
|
||||||
|
- Allow target names to be arbitrary expressions so loops can generate targets.
|
||||||
|
- Keep quoted static target names as real named procedures such as makefile-target-stuff-all.
|
||||||
|
- Restore and test examples/generated-targets.rkt with targets generated from for/list.
|
||||||
|
|
||||||
0.3.6
|
0.3.6
|
||||||
- Restore raco as a small pure-Racket helper in the default racket-makefile API.
|
- Restore raco as a small pure-Racket helper in the default racket-makefile API.
|
||||||
- Remove the Rash adapter and Rash dependencies from racket-makefile.
|
- Remove the Rash adapter and Rash dependencies from racket-makefile.
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
(require "main.rkt")
|
(require "main.rkt")
|
||||||
|
|
||||||
(makefile racket-makefile
|
(makefile 'racket-makefile
|
||||||
(default-target all)
|
(default-target 'all)
|
||||||
(phony all commit push status pull clean package doc showdoc refresh)
|
(phony 'all 'commit 'push 'status 'pull 'clean 'package 'doc 'showdoc 'refresh)
|
||||||
|
|
||||||
(target all
|
(target 'all
|
||||||
(displayln "use (make 'clean), (make 'package), or (make 'commit)"))
|
(displayln "use (make 'clean), (make 'package), or (make 'commit)"))
|
||||||
|
|
||||||
(target commit
|
(target 'commit
|
||||||
(let ([st (git 'status)])
|
(let ([st (git 'status)])
|
||||||
(displayln st)
|
(displayln st)
|
||||||
(when (> (length st) 0)
|
(when (> (length st) 0)
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
(git 'commit)
|
(git 'commit)
|
||||||
(git 'push))))
|
(git 'push))))
|
||||||
|
|
||||||
(target push
|
(target 'push
|
||||||
(git 'push))
|
(git 'push))
|
||||||
|
|
||||||
(target status
|
(target 'status
|
||||||
(displayln "git status:")
|
(displayln "git status:")
|
||||||
(for-each
|
(for-each
|
||||||
(λ (e)
|
(λ (e)
|
||||||
@@ -28,10 +28,10 @@
|
|||||||
(format "~a\t:\t~a (remote ~a)" (caddr e) (cadr e) (car e))))
|
(format "~a\t:\t~a (remote ~a)" (caddr e) (cadr e) (car e))))
|
||||||
(git 'status)))
|
(git 'status)))
|
||||||
|
|
||||||
(target pull
|
(target 'pull
|
||||||
(git 'pull))
|
(git 'pull))
|
||||||
|
|
||||||
(target clean
|
(target 'clean
|
||||||
|
|
||||||
(for-each (λ (f) (displayln f) (rm-f f))
|
(for-each (λ (f) (displayln f) (rm-f f))
|
||||||
(list-files "." #px"([.]bak|~)$" #:recursive #t))
|
(list-files "." #px"([.]bak|~)$" #:recursive #t))
|
||||||
@@ -40,15 +40,15 @@
|
|||||||
(for-each (λ (f) (displayln f) (rm-f f))
|
(for-each (λ (f) (displayln f) (rm-f f))
|
||||||
(list-files "scrbl" #px"[.](css|js|html)$")))
|
(list-files "scrbl" #px"[.](css|js|html)$")))
|
||||||
|
|
||||||
(target version
|
(target 'version
|
||||||
(displayln (format "Next version: ~a" (git 'next-version))))
|
(displayln (format "Next version: ~a" (git 'next-version))))
|
||||||
|
|
||||||
(target package
|
(target 'package
|
||||||
(deps clean)
|
(deps 'clean)
|
||||||
(zip-package))
|
(zip-package))
|
||||||
|
|
||||||
(target zip
|
(target 'zip
|
||||||
(deps package))
|
(deps 'package))
|
||||||
|
|
||||||
(target "docs/racket-makefile.html"
|
(target "docs/racket-makefile.html"
|
||||||
(deps "scrbl/racket-makefile.scrbl")
|
(deps "scrbl/racket-makefile.scrbl")
|
||||||
@@ -56,15 +56,15 @@
|
|||||||
(make-directory "docs"))
|
(make-directory "docs"))
|
||||||
(raco '(scribble --html +m --dest "docs" scrbl/racket-makefile.scrbl)))
|
(raco '(scribble --html +m --dest "docs" scrbl/racket-makefile.scrbl)))
|
||||||
|
|
||||||
(target doc
|
(target 'doc
|
||||||
(deps "docs/racket-makefile.html")
|
(deps "docs/racket-makefile.html")
|
||||||
(displayln "Documentation built"))
|
(displayln "Documentation built"))
|
||||||
|
|
||||||
(target showdoc
|
(target 'showdoc
|
||||||
(deps doc)
|
(deps 'doc)
|
||||||
(send-url/file "docs/racket-makefile.html"))
|
(send-url/file "docs/racket-makefile.html"))
|
||||||
|
|
||||||
(target refresh
|
(target 'refresh
|
||||||
(displayln "Refreshing makefile")
|
(displayln "Refreshing makefile")
|
||||||
(refresh-makefile)
|
(refresh-makefile)
|
||||||
(displayln "done.")))
|
(displayln "done.")))
|
||||||
|
|||||||
@@ -5,27 +5,29 @@
|
|||||||
|
|
||||||
## Functional makefiles
|
## Functional makefiles
|
||||||
|
|
||||||
|
A makefile is ordinary Racket code inside a `makefile` form. The prefix and static symbolic target names are explicit values:
|
||||||
|
|
||||||
```racket
|
```racket
|
||||||
#lang racket
|
#lang racket
|
||||||
|
|
||||||
(require racket-makefile)
|
(require racket-makefile)
|
||||||
|
|
||||||
(makefile wiki
|
(makefile 'wiki
|
||||||
(default-target all)
|
(default-target 'all)
|
||||||
(phony all clean status)
|
(phony 'all 'clean 'status)
|
||||||
|
|
||||||
(target status
|
(target 'status
|
||||||
(displayln "status"))
|
(displayln "status"))
|
||||||
|
|
||||||
(target clean
|
(target 'clean
|
||||||
(rm-rf "compiled"))
|
(rm-rf "compiled"))
|
||||||
|
|
||||||
(target all
|
(target 'all
|
||||||
(deps status)
|
(deps 'status)
|
||||||
(displayln "all")))
|
(displayln "all")))
|
||||||
```
|
```
|
||||||
|
|
||||||
A `makefile` form defines real target procedures. The example above defines, among others:
|
A statically quoted target name defines a real named procedure. The example above defines, among others:
|
||||||
|
|
||||||
```racket
|
```racket
|
||||||
makefile-target-wiki-status
|
makefile-target-wiki-status
|
||||||
@@ -40,7 +42,7 @@ They are ordinary procedures:
|
|||||||
(makefile-target-wiki-status)
|
(makefile-target-wiki-status)
|
||||||
```
|
```
|
||||||
|
|
||||||
`make` is also an ordinary procedure. Target names are explicit Racket values:
|
`make` is also an ordinary procedure:
|
||||||
|
|
||||||
```racket
|
```racket
|
||||||
(make)
|
(make)
|
||||||
@@ -48,18 +50,48 @@ They are ordinary procedures:
|
|||||||
(make 'clean 'all)
|
(make 'clean 'all)
|
||||||
```
|
```
|
||||||
|
|
||||||
`make` resolves target names for the active makefile in the global `makefile-targets` registry. It handles dependencies and timestamp checks and invokes the registered target procedure when the target needs to run.
|
`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
|
## Makefile prefixes
|
||||||
|
|
||||||
Every makefile has a prefix:
|
Every makefile has an explicit prefix:
|
||||||
|
|
||||||
```racket
|
```racket
|
||||||
(makefile wiki
|
(makefile 'wiki
|
||||||
(target status ...))
|
(target 'status ...))
|
||||||
|
|
||||||
(makefile audio
|
(makefile 'audio
|
||||||
(target status ...))
|
(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:
|
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:
|
||||||
@@ -81,30 +113,47 @@ Targets with the same name can therefore coexist. Evaluating a `makefile` form m
|
|||||||
|
|
||||||
Re-evaluating a makefile with the same prefix replaces the registrations for that prefix.
|
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
|
||||||
|
|
||||||
Rash integration is intentionally kept out of this package. Install and require the separate `rash-makefile` package when `make target` line syntax is desired.
|
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
|
## Targets and dependencies
|
||||||
|
|
||||||
A target is declared only inside `makefile`:
|
`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
|
```racket
|
||||||
(makefile example
|
(makefile 'example
|
||||||
(target "program"
|
(define prerequisites '("one.c" "two.c"))
|
||||||
(deps "program.c")
|
|
||||||
(run '(cc -o program program.c))))
|
|
||||||
```
|
|
||||||
|
|
||||||
Target and makefile names are literal identifiers, strings, paths, or quoted symbols. Dependencies may be expressions and may return nested lists; the build engine flattens them.
|
(target 'program
|
||||||
|
(deps prerequisites)
|
||||||
`phony` and `default-target` are also makefile clauses:
|
(run '(cc -o program one.c two.c))))
|
||||||
|
|
||||||
```racket
|
|
||||||
(makefile example
|
|
||||||
(default-target all)
|
|
||||||
(phony all clean)
|
|
||||||
...)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
The automatic recipe values remain available inside target procedures:
|
The automatic recipe values remain available inside target procedures:
|
||||||
@@ -115,7 +164,7 @@ $deps
|
|||||||
$<
|
$<
|
||||||
```
|
```
|
||||||
|
|
||||||
Because a target is a real procedure, calling the generated procedure directly runs its recipe directly. Calling it through `make` adds dependency traversal and timestamp-based rebuilding.
|
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
|
## Refreshing
|
||||||
|
|
||||||
|
|||||||
@@ -8,23 +8,23 @@
|
|||||||
(define CC 'cc)
|
(define CC 'cc)
|
||||||
(define CFLAGS '(-Wall -O2))
|
(define CFLAGS '(-Wall -O2))
|
||||||
|
|
||||||
(makefile hello
|
(makefile 'hello
|
||||||
(default-target all)
|
(default-target 'all)
|
||||||
(phony all clean setup test)
|
(phony 'all 'clean 'setup 'test)
|
||||||
|
|
||||||
(target all
|
(target 'all
|
||||||
(deps "hello"))
|
(deps "hello"))
|
||||||
|
|
||||||
(target "hello"
|
(target "hello"
|
||||||
(deps "hello.c")
|
(deps "hello.c")
|
||||||
(run `(,CC ,@CFLAGS -o $target $<)))
|
(run `(,CC ,@CFLAGS -o $target $<)))
|
||||||
|
|
||||||
(target clean
|
(target 'clean
|
||||||
(rm-f "hello")
|
(rm-f "hello")
|
||||||
(rm-rf "compiled"))
|
(rm-rf "compiled"))
|
||||||
|
|
||||||
(target setup
|
(target 'setup
|
||||||
(raco '(setup racket-makefile)))
|
(raco '(setup racket-makefile)))
|
||||||
|
|
||||||
(target test
|
(target 'test
|
||||||
(raco '(test -p racket-makefile))))
|
(raco '(test -p racket-makefile))))
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
|
|
||||||
(require racket-makefile)
|
(require racket-makefile)
|
||||||
|
|
||||||
(makefile cleanup-example
|
(makefile 'cleanup-example
|
||||||
(default-target all)
|
(default-target 'all)
|
||||||
(phony all clean)
|
(phony 'all 'clean)
|
||||||
|
|
||||||
(target all
|
(target 'all
|
||||||
(displayln "use (make 'clean)"))
|
(displayln "use (make 'clean)"))
|
||||||
|
|
||||||
(target clean
|
(target 'clean
|
||||||
(display "cleaning up...")
|
(display "cleaning up...")
|
||||||
(apply rm-rf
|
(apply rm-rf
|
||||||
(list-dirs "." #px"compiled$" #:recursive #t))
|
(list-dirs "." #px"compiled$" #:recursive #t))
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
#lang racket-makefile
|
#lang racket
|
||||||
|
|
||||||
;; Ordinary Racket can generate targets and dependency lists.
|
(require racket-makefile)
|
||||||
|
|
||||||
(define sources '("foo.c" "bar.c" "baz.c"))
|
;; A makefile body is ordinary Racket. Target names that are expressions are
|
||||||
|
;; evaluated while the makefile is registered, so a loop can generate targets.
|
||||||
|
|
||||||
(define objects
|
(makefile 'stuff
|
||||||
|
(define sources '("foo.c" "bar.c" "baz.c"))
|
||||||
|
|
||||||
|
(define objects
|
||||||
(for/list ([src sources])
|
(for/list ([src sources])
|
||||||
(define obj (path-replace-extension src #".o"))
|
(define obj (path-replace-extension src #".o"))
|
||||||
(target obj
|
(target obj
|
||||||
@@ -12,11 +16,11 @@
|
|||||||
(run `(cc -c $< -o $target)))
|
(run `(cc -c $< -o $target)))
|
||||||
obj))
|
obj))
|
||||||
|
|
||||||
(default-target all)
|
(default-target 'all)
|
||||||
(phony all clean)
|
(phony 'all 'clean)
|
||||||
|
|
||||||
(target all
|
(target 'all
|
||||||
(deps objects))
|
(deps objects))
|
||||||
|
|
||||||
(target clean
|
(target 'clean
|
||||||
(apply rm-f objects))
|
(apply rm-f objects)))
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#lang info
|
#lang info
|
||||||
|
|
||||||
(define pkg-authors '(hnmdijkema))
|
(define pkg-authors '(hnmdijkema))
|
||||||
(define version "0.3.6")
|
(define version "0.3.8")
|
||||||
(define license 'MIT)
|
(define license 'MIT)
|
||||||
(define collection "racket-makefile")
|
(define collection "racket-makefile")
|
||||||
(define pkg-desc
|
(define pkg-desc
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
|
||||||
(require racket
|
(require racket
|
||||||
|
racket/splicing
|
||||||
|
racket/stxparam
|
||||||
(for-syntax racket/base
|
(for-syntax racket/base
|
||||||
racket/list
|
racket/stxparam
|
||||||
racket/string
|
|
||||||
syntax/parse)
|
syntax/parse)
|
||||||
"private/commands.rkt"
|
"private/commands.rkt"
|
||||||
"private/engine.rkt"
|
"private/engine.rkt"
|
||||||
@@ -19,7 +20,10 @@
|
|||||||
default-target
|
default-target
|
||||||
make
|
make
|
||||||
current-makefile-prefix
|
current-makefile-prefix
|
||||||
|
makefile-prefixes
|
||||||
makefile-targets
|
makefile-targets
|
||||||
|
makefile-target-exists?
|
||||||
|
makefile-target-procedure
|
||||||
refresh-makefile
|
refresh-makefile
|
||||||
run
|
run
|
||||||
raco
|
raco
|
||||||
@@ -86,47 +90,37 @@
|
|||||||
(or (current-first-dependency)
|
(or (current-first-dependency)
|
||||||
(error '$< "$< is only available while a target procedure with dependencies is running"))]))
|
(error '$< "$< is only available while a target procedure with dependencies is running"))]))
|
||||||
|
|
||||||
(define-syntax (target stx)
|
(define-syntax-parameter makefile-prefix
|
||||||
(raise-syntax-error 'target "only valid inside makefile" stx))
|
(λ (stx)
|
||||||
|
(raise-syntax-error 'racket-makefile "only valid inside makefile" stx)))
|
||||||
(define-syntax (deps stx)
|
|
||||||
(raise-syntax-error 'deps "only valid inside a target clause in makefile" stx))
|
|
||||||
|
|
||||||
(define-syntax (phony stx)
|
|
||||||
(raise-syntax-error 'phony "only valid inside makefile" stx))
|
|
||||||
|
|
||||||
(define-syntax (default-target stx)
|
|
||||||
(raise-syntax-error 'default-target "only valid inside makefile" stx))
|
|
||||||
|
|
||||||
(begin-for-syntax
|
(begin-for-syntax
|
||||||
(struct target-spec (name-datum name-expression dependencies body source) #:transparent)
|
(define (makefile-prefix-expression stx)
|
||||||
|
(define transformer (syntax-parameter-value #'makefile-prefix))
|
||||||
|
(transformer stx))
|
||||||
|
|
||||||
(define (literal-name-datum stx who)
|
(define (makefile-prefix-datum stx)
|
||||||
|
(define expression (makefile-prefix-expression stx))
|
||||||
|
(syntax-parse expression
|
||||||
|
[((~datum quote) value)
|
||||||
|
(syntax-e #'value)]
|
||||||
|
[_
|
||||||
|
(raise-syntax-error
|
||||||
|
'makefile
|
||||||
|
"makefile prefix must be a quoted symbol or string"
|
||||||
|
stx)]))
|
||||||
|
|
||||||
|
(define (static-name-datum stx)
|
||||||
(syntax-parse stx
|
(syntax-parse stx
|
||||||
[id:id
|
|
||||||
(syntax-e #'id)]
|
|
||||||
[s:str
|
|
||||||
(syntax-e #'s)]
|
|
||||||
[((~datum quote) value)
|
[((~datum quote) value)
|
||||||
(define datum (syntax-e #'value))
|
(define datum (syntax-e #'value))
|
||||||
(unless (or (symbol? datum) (string? datum) (path? datum))
|
(unless (or (symbol? datum) (string? datum) (path? datum))
|
||||||
(raise-syntax-error who "expected a symbol, string, or path target name" stx))
|
|
||||||
datum]
|
|
||||||
[_
|
|
||||||
(raise-syntax-error
|
(raise-syntax-error
|
||||||
who
|
'target
|
||||||
"target and makefile names must be literal identifiers, strings, paths, or quoted symbols"
|
"quoted target name must be a symbol, string, or path"
|
||||||
stx)]))
|
|
||||||
|
|
||||||
(define (literal-expression stx who)
|
|
||||||
(define datum (literal-name-datum stx who))
|
|
||||||
#`(quote #,datum))
|
|
||||||
|
|
||||||
(define (dependency-expression stx)
|
|
||||||
(if (and (identifier? stx)
|
|
||||||
(not (identifier-binding stx)))
|
|
||||||
#`(quote #,(syntax-e stx))
|
|
||||||
stx))
|
stx))
|
||||||
|
datum]
|
||||||
|
[_ #f]))
|
||||||
|
|
||||||
(define (procedure-identifier context prefix target)
|
(define (procedure-identifier context prefix target)
|
||||||
(define name
|
(define name
|
||||||
@@ -134,96 +128,134 @@
|
|||||||
(format "makefile-target-~a-~a" prefix target)))
|
(format "makefile-target-~a-~a" prefix target)))
|
||||||
(datum->syntax context name context context))
|
(datum->syntax context name context context))
|
||||||
|
|
||||||
(define (parse-target clause)
|
(define (dynamic-target-expression prefix-expression name dependencies body)
|
||||||
(syntax-parse clause
|
#`(let* ([target-name #,name]
|
||||||
#:datum-literals (target deps)
|
[target-procedure
|
||||||
[(target name (deps dependency ...) body ...)
|
(procedure-rename
|
||||||
(define datum (literal-name-datum #'name 'target))
|
(λ ()
|
||||||
(target-spec datum
|
(call-target-procedure
|
||||||
(literal-expression #'name 'target)
|
|
||||||
(map dependency-expression
|
|
||||||
(syntax->list #'(dependency ...)))
|
|
||||||
(syntax->list #'(body ...))
|
|
||||||
clause)]
|
|
||||||
[(target name body ...)
|
|
||||||
(define datum (literal-name-datum #'name 'target))
|
|
||||||
(target-spec datum
|
|
||||||
(literal-expression #'name 'target)
|
|
||||||
'()
|
|
||||||
(syntax->list #'(body ...))
|
|
||||||
clause)]))
|
|
||||||
)
|
|
||||||
|
|
||||||
(define-syntax (makefile stx)
|
|
||||||
(syntax-parse stx
|
|
||||||
#:datum-literals (target phony default-target)
|
|
||||||
[(_ prefix clause ...)
|
|
||||||
(define prefix-datum (literal-name-datum #'prefix 'makefile))
|
|
||||||
(define prefix-expression #`(quote #,prefix-datum))
|
|
||||||
(define clauses (syntax->list #'(clause ...)))
|
|
||||||
|
|
||||||
(define targets '())
|
|
||||||
(define phony-forms '())
|
|
||||||
(define default-forms '())
|
|
||||||
|
|
||||||
(for ([clause (in-list clauses)])
|
|
||||||
(syntax-parse clause
|
|
||||||
#:datum-literals (target phony default-target)
|
|
||||||
[(target . _)
|
|
||||||
(set! targets (append targets (list (parse-target clause))))]
|
|
||||||
[(phony name ...)
|
|
||||||
(define names
|
|
||||||
(for/list ([name (in-list (syntax->list #'(name ...)))])
|
|
||||||
(literal-expression name 'phony)))
|
|
||||||
(set! phony-forms
|
|
||||||
(append phony-forms
|
|
||||||
(list #`(mark-phony! #,prefix-expression #,@names))))]
|
|
||||||
[(default-target name)
|
|
||||||
(define name-expression (literal-expression #'name 'default-target))
|
|
||||||
(set! default-forms
|
|
||||||
(append default-forms
|
|
||||||
(list #`(set-default-target!
|
|
||||||
#,prefix-expression
|
#,prefix-expression
|
||||||
#,name-expression))))]
|
target-name
|
||||||
[_
|
(λ ()
|
||||||
(raise-syntax-error
|
#,@body
|
||||||
'makefile
|
(void))))
|
||||||
"expected target, phony, or default-target clause"
|
(string->symbol
|
||||||
clause)]))
|
(format "makefile-target-~a-~a"
|
||||||
|
#,prefix-expression
|
||||||
|
target-name)))])
|
||||||
|
(register-target!
|
||||||
|
#,prefix-expression
|
||||||
|
target-name
|
||||||
|
(list #,@dependencies)
|
||||||
|
target-procedure)
|
||||||
|
(void))))
|
||||||
|
|
||||||
(define target-definitions
|
(define-syntax (target stx)
|
||||||
(for/list ([spec (in-list targets)])
|
(syntax-parse stx
|
||||||
(define proc-id
|
#:datum-literals (deps)
|
||||||
(procedure-identifier stx prefix-datum (target-spec-name-datum spec)))
|
[(_ name (deps dependency ...) body ...)
|
||||||
(define name-expression (target-spec-name-expression spec))
|
(define prefix-expression (makefile-prefix-expression stx))
|
||||||
(define dependencies (target-spec-dependencies spec))
|
(define prefix-datum (makefile-prefix-datum stx))
|
||||||
(define body (target-spec-body spec))
|
(define static-name (static-name-datum #'name))
|
||||||
|
(define dependencies (syntax->list #'(dependency ...)))
|
||||||
|
(define body-list (syntax->list #'(body ...)))
|
||||||
|
|
||||||
|
(if (and static-name
|
||||||
|
(not (eq? (syntax-local-context) 'expression)))
|
||||||
|
(let ([proc-id
|
||||||
|
(procedure-identifier stx prefix-datum static-name)])
|
||||||
#`(begin
|
#`(begin
|
||||||
(define (#,proc-id)
|
(define (#,proc-id)
|
||||||
(call-target-procedure
|
(call-target-procedure
|
||||||
#,prefix-expression
|
#,prefix-expression
|
||||||
#,name-expression
|
name
|
||||||
(λ ()
|
(λ ()
|
||||||
#,@body
|
body ...
|
||||||
(void))))
|
(void))))
|
||||||
(register-target!
|
(register-target!
|
||||||
#,prefix-expression
|
#,prefix-expression
|
||||||
#,name-expression
|
name
|
||||||
(list #,@dependencies)
|
(list dependency ...)
|
||||||
#,proc-id))))
|
#,proc-id)))
|
||||||
|
(dynamic-target-expression
|
||||||
|
prefix-expression
|
||||||
|
#'name
|
||||||
|
dependencies
|
||||||
|
body-list))]
|
||||||
|
|
||||||
|
[(_ name body ...)
|
||||||
|
(define prefix-expression (makefile-prefix-expression stx))
|
||||||
|
(define prefix-datum (makefile-prefix-datum stx))
|
||||||
|
(define static-name (static-name-datum #'name))
|
||||||
|
(define body-list (syntax->list #'(body ...)))
|
||||||
|
|
||||||
|
(if (and static-name
|
||||||
|
(not (eq? (syntax-local-context) 'expression)))
|
||||||
|
(let ([proc-id
|
||||||
|
(procedure-identifier stx prefix-datum static-name)])
|
||||||
|
#`(begin
|
||||||
|
(define (#,proc-id)
|
||||||
|
(call-target-procedure
|
||||||
|
#,prefix-expression
|
||||||
|
name
|
||||||
|
(λ ()
|
||||||
|
body ...
|
||||||
|
(void))))
|
||||||
|
(register-target!
|
||||||
|
#,prefix-expression
|
||||||
|
name
|
||||||
|
'()
|
||||||
|
#,proc-id)))
|
||||||
|
(dynamic-target-expression
|
||||||
|
prefix-expression
|
||||||
|
#'name
|
||||||
|
'()
|
||||||
|
body-list))]))
|
||||||
|
|
||||||
|
(define-syntax (deps stx)
|
||||||
|
(raise-syntax-error 'deps "only valid as the dependency clause of target" stx))
|
||||||
|
|
||||||
|
(define-syntax (phony stx)
|
||||||
|
(syntax-parse stx
|
||||||
|
[(_ name ...)
|
||||||
|
(define prefix-expression (makefile-prefix-expression stx))
|
||||||
|
#`(mark-phony! #,prefix-expression name ...)]))
|
||||||
|
|
||||||
|
(define-syntax (default-target stx)
|
||||||
|
(syntax-parse stx
|
||||||
|
[(_ name)
|
||||||
|
(define prefix-expression (makefile-prefix-expression stx))
|
||||||
|
#`(set-default-target! #,prefix-expression name)]))
|
||||||
|
|
||||||
|
(define-syntax (makefile stx)
|
||||||
|
(syntax-parse stx
|
||||||
|
[(_ ((~datum quote) prefix) form ...)
|
||||||
|
(define prefix-datum (syntax-e #'prefix))
|
||||||
|
(unless (or (symbol? prefix-datum) (string? prefix-datum))
|
||||||
|
(raise-syntax-error
|
||||||
|
'makefile
|
||||||
|
"prefix must be a quoted symbol or string"
|
||||||
|
#'prefix))
|
||||||
|
|
||||||
|
(define prefix-expression #`(quote #,prefix-datum))
|
||||||
(define source (syntax-source stx))
|
(define source (syntax-source stx))
|
||||||
(define remember-source
|
(define remember-source
|
||||||
(if (path? source)
|
(if (path? source)
|
||||||
#`(remember-makefile! (string->path #,(path->string source)))
|
#`(remember-makefile! (string->path #,(path->string source)))
|
||||||
#'(void)))
|
#'(void)))
|
||||||
|
|
||||||
|
(with-syntax ([prefix-expression prefix-expression])
|
||||||
#`(begin
|
#`(begin
|
||||||
#,remember-source
|
#,remember-source
|
||||||
(begin-makefile! #,prefix-expression)
|
(begin-makefile! prefix-expression)
|
||||||
#,@target-definitions
|
(splicing-syntax-parameterize
|
||||||
#,@phony-forms
|
([makefile-prefix (λ (stx) #'prefix-expression)])
|
||||||
#,@default-forms
|
form ...)
|
||||||
;; The last makefile definition evaluated is the active makefile.
|
(current-makefile-prefix prefix-expression)
|
||||||
(current-makefile-prefix #,prefix-expression)
|
(void)))]
|
||||||
(void))]))
|
|
||||||
|
[(_ prefix form ...)
|
||||||
|
(raise-syntax-error
|
||||||
|
'makefile
|
||||||
|
"prefix must be explicit, for example (makefile 'stuff ...)"
|
||||||
|
#'prefix)]))
|
||||||
|
|||||||
+66
-10
@@ -2,7 +2,10 @@
|
|||||||
|
|
||||||
(require racket/list)
|
(require racket/list)
|
||||||
|
|
||||||
(provide makefile-targets
|
(provide makefile-prefixes
|
||||||
|
makefile-targets
|
||||||
|
makefile-target-exists?
|
||||||
|
makefile-target-procedure
|
||||||
current-makefile-prefix
|
current-makefile-prefix
|
||||||
begin-makefile!
|
begin-makefile!
|
||||||
register-target!
|
register-target!
|
||||||
@@ -18,8 +21,11 @@
|
|||||||
;; Target procedures are registered globally per makefile prefix. The value is
|
;; Target procedures are registered globally per makefile prefix. The value is
|
||||||
;; deliberately the real target procedure; `make` ultimately resolves a target
|
;; deliberately the real target procedure; `make` ultimately resolves a target
|
||||||
;; in this hash and calls that procedure.
|
;; in this hash and calls that procedure.
|
||||||
(define makefile-targets (make-hash))
|
(define makefile-target-registry (make-hash))
|
||||||
(define target-dependencies (make-hash))
|
(define target-dependencies (make-hash))
|
||||||
|
(define target-values (make-hash))
|
||||||
|
(define makefile-prefix-values (make-hash))
|
||||||
|
(define makefile-prefix-order '())
|
||||||
(define phony-targets (make-hash))
|
(define phony-targets (make-hash))
|
||||||
(define target-order (make-hash))
|
(define target-order (make-hash))
|
||||||
(define default-targets (make-hash))
|
(define default-targets (make-hash))
|
||||||
@@ -57,22 +63,33 @@
|
|||||||
|
|
||||||
(define (clear-prefix! prefix)
|
(define (clear-prefix! prefix)
|
||||||
(define pkey (prefix-key prefix))
|
(define pkey (prefix-key prefix))
|
||||||
(for ([key (in-list (hash-keys makefile-targets))])
|
(for ([key (in-list (hash-keys makefile-target-registry))])
|
||||||
(when (equal? (car key) pkey)
|
(when (equal? (car key) pkey)
|
||||||
(hash-remove! makefile-targets key)
|
(hash-remove! makefile-target-registry key)
|
||||||
(hash-remove! target-dependencies key)
|
(hash-remove! target-dependencies key)
|
||||||
|
(hash-remove! target-values key)
|
||||||
(hash-remove! phony-targets key)))
|
(hash-remove! phony-targets key)))
|
||||||
(hash-remove! target-order pkey)
|
(hash-remove! target-order pkey)
|
||||||
(hash-remove! default-targets pkey)
|
(hash-remove! default-targets pkey)
|
||||||
(void))
|
(void))
|
||||||
|
|
||||||
(define (begin-makefile! prefix)
|
(define (begin-makefile! prefix)
|
||||||
|
(define pkey (prefix-key prefix))
|
||||||
(clear-prefix! prefix)
|
(clear-prefix! prefix)
|
||||||
|
(hash-set! makefile-prefix-values pkey prefix)
|
||||||
|
(set! makefile-prefix-order
|
||||||
|
(append
|
||||||
|
(filter (λ (existing) (not (equal? existing pkey)))
|
||||||
|
makefile-prefix-order)
|
||||||
|
(list pkey)))
|
||||||
(void))
|
(void))
|
||||||
|
|
||||||
(define (reset-makefiles!)
|
(define (reset-makefiles!)
|
||||||
(hash-clear! makefile-targets)
|
(hash-clear! makefile-target-registry)
|
||||||
(hash-clear! target-dependencies)
|
(hash-clear! target-dependencies)
|
||||||
|
(hash-clear! target-values)
|
||||||
|
(hash-clear! makefile-prefix-values)
|
||||||
|
(set! makefile-prefix-order '())
|
||||||
(hash-clear! phony-targets)
|
(hash-clear! phony-targets)
|
||||||
(hash-clear! target-order)
|
(hash-clear! target-order)
|
||||||
(hash-clear! default-targets)
|
(hash-clear! default-targets)
|
||||||
@@ -84,12 +101,13 @@
|
|||||||
(define tkey (target-key name))
|
(define tkey (target-key name))
|
||||||
(define key (cons pkey tkey))
|
(define key (cons pkey tkey))
|
||||||
|
|
||||||
(unless (hash-has-key? makefile-targets key)
|
(unless (hash-has-key? makefile-target-registry key)
|
||||||
(hash-set! target-order
|
(hash-set! target-order
|
||||||
pkey
|
pkey
|
||||||
(append (hash-ref target-order pkey '()) (list tkey))))
|
(append (hash-ref target-order pkey '()) (list tkey))))
|
||||||
|
|
||||||
(hash-set! makefile-targets key procedure)
|
(hash-set! makefile-target-registry key procedure)
|
||||||
|
(hash-set! target-values key name)
|
||||||
(hash-set! target-dependencies key (append-map target-keys dependencies))
|
(hash-set! target-dependencies key (append-map target-keys dependencies))
|
||||||
(void))
|
(void))
|
||||||
|
|
||||||
@@ -103,8 +121,45 @@
|
|||||||
(hash-set! default-targets (prefix-key prefix) (target-key name))
|
(hash-set! default-targets (prefix-key prefix) (target-key name))
|
||||||
(void))
|
(void))
|
||||||
|
|
||||||
|
(define (require-current-prefix who)
|
||||||
|
(define prefix (current-makefile-prefix))
|
||||||
|
(unless prefix
|
||||||
|
(error who "no current makefile prefix; evaluate a makefile definition first"))
|
||||||
|
prefix)
|
||||||
|
|
||||||
|
(define (makefile-prefixes)
|
||||||
|
(for/list ([pkey (in-list makefile-prefix-order)])
|
||||||
|
(hash-ref makefile-prefix-values pkey)))
|
||||||
|
|
||||||
|
(define makefile-targets
|
||||||
|
(case-lambda
|
||||||
|
[()
|
||||||
|
(makefile-targets (require-current-prefix 'makefile-targets))]
|
||||||
|
[(prefix)
|
||||||
|
(define pkey (prefix-key prefix))
|
||||||
|
(for/list ([tkey (in-list (hash-ref target-order pkey '()))])
|
||||||
|
(hash-ref target-values (cons pkey tkey) tkey))]))
|
||||||
|
|
||||||
|
(define makefile-target-exists?
|
||||||
|
(case-lambda
|
||||||
|
[(name)
|
||||||
|
(makefile-target-exists?
|
||||||
|
(require-current-prefix 'makefile-target-exists?)
|
||||||
|
name)]
|
||||||
|
[(prefix name)
|
||||||
|
(hash-has-key? makefile-target-registry (registry-key prefix name))]))
|
||||||
|
|
||||||
|
(define makefile-target-procedure
|
||||||
|
(case-lambda
|
||||||
|
[(name)
|
||||||
|
(makefile-target-procedure
|
||||||
|
(require-current-prefix 'makefile-target-procedure)
|
||||||
|
name)]
|
||||||
|
[(prefix name)
|
||||||
|
(target-procedure prefix name)]))
|
||||||
|
|
||||||
(define (target-procedure prefix name)
|
(define (target-procedure prefix name)
|
||||||
(hash-ref makefile-targets
|
(hash-ref makefile-target-registry
|
||||||
(registry-key prefix name)
|
(registry-key prefix name)
|
||||||
(λ ()
|
(λ ()
|
||||||
(error 'racket-makefile
|
(error 'racket-makefile
|
||||||
@@ -131,7 +186,7 @@
|
|||||||
(file-or-directory-modify-seconds path #f (λ () #f)))
|
(file-or-directory-modify-seconds path #f (λ () #f)))
|
||||||
|
|
||||||
(define (registered-target? prefix name)
|
(define (registered-target? prefix name)
|
||||||
(hash-has-key? makefile-targets (registry-key prefix name)))
|
(hash-has-key? makefile-target-registry (registry-key prefix name)))
|
||||||
|
|
||||||
(define (dependency-time prefix name built-results)
|
(define (dependency-time prefix name built-results)
|
||||||
(cond
|
(cond
|
||||||
@@ -159,7 +214,8 @@
|
|||||||
(and dep-time (> dep-time target-time)))])]))
|
(and dep-time (> dep-time target-time)))])]))
|
||||||
|
|
||||||
(define (execute-target! prefix name)
|
(define (execute-target! prefix name)
|
||||||
(printf "racket-makefile[~a]: ~a\n" prefix (target-key name))
|
(printf "racket-makefile[~a]: ~a
|
||||||
|
" prefix (target-key name))
|
||||||
((target-procedure prefix name))
|
((target-procedure prefix name))
|
||||||
(void))
|
(void))
|
||||||
|
|
||||||
|
|||||||
+66
-22
@@ -13,27 +13,27 @@
|
|||||||
|
|
||||||
@section{A functional makefile}
|
@section{A functional makefile}
|
||||||
|
|
||||||
A makefile is declared with one prefix and a set of target clauses:
|
A makefile is ordinary Racket code inside @racket[makefile]. The prefix and static symbolic target names are explicit values:
|
||||||
|
|
||||||
@racketblock[
|
@racketblock[
|
||||||
(require racket-makefile)
|
(require racket-makefile)
|
||||||
|
|
||||||
(makefile wiki
|
(makefile 'wiki
|
||||||
(default-target all)
|
(default-target 'all)
|
||||||
(phony status clean all)
|
(phony 'status 'clean 'all)
|
||||||
|
|
||||||
(target status
|
(target 'status
|
||||||
(displayln "status"))
|
(displayln "status"))
|
||||||
|
|
||||||
(target clean
|
(target 'clean
|
||||||
(rm-rf "compiled"))
|
(rm-rf "compiled"))
|
||||||
|
|
||||||
(target all
|
(target 'all
|
||||||
(deps status)
|
(deps 'status)
|
||||||
(displayln "all")))
|
(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.
|
A statically quoted target name defines a real Racket procedure binding. 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[
|
@racketblock[
|
||||||
(procedure? makefile-target-wiki-status)
|
(procedure? makefile-target-wiki-status)
|
||||||
@@ -44,35 +44,65 @@ Calling a generated target procedure directly executes the recipe directly. Call
|
|||||||
|
|
||||||
@section{Makefile definitions}
|
@section{Makefile definitions}
|
||||||
|
|
||||||
@defform[(makefile prefix clause ...)]{
|
@defform[(makefile prefix form ...)]{
|
||||||
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.
|
Defines one prefixed makefile and evaluates @racket[form ...] as ordinary Racket code in the lexical context of that makefile. The @racket[prefix] is explicit, normally a quoted symbol such as @racket['wiki].
|
||||||
|
|
||||||
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.
|
Before the body is evaluated, registrations for the same prefix are removed. After the complete body has been evaluated, @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].
|
Ordinary definitions, loops, conditionals, and other Racket forms may occur in the body. The declaration forms @racket[target], @racket[phony], and @racket[default-target] obtain the surrounding prefix lexically and can therefore also occur inside nested Racket forms.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defform[(target name (deps dependency ...) body ...)]{
|
@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 ...].
|
Registers one target procedure for the surrounding makefile. The @racket[name] is an ordinary Racket expression.
|
||||||
|
|
||||||
Dependency expressions are evaluated when the makefile is registered. A dependency expression may produce nested lists; the build engine flattens them.
|
When @racket[name] is a quoted static value, for example @racket['status], the macro also defines a normal Racket procedure binding. In a makefile with prefix @racket['wiki], @racket[(target 'status ...)] defines @racket[makefile-target-wiki-status].
|
||||||
|
|
||||||
|
When @racket[name] is an expression such as a variable, the expression is evaluated while the surrounding makefile body runs. The resulting procedure is registered dynamically and closes over the lexical values used by its recipe. This allows ordinary loops to generate targets.
|
||||||
|
|
||||||
|
Dependency expressions are evaluated when the target is registered. A dependency expression may produce nested lists; the build engine flattens them.
|
||||||
}
|
}
|
||||||
|
|
||||||
A target without a @racket[deps] clause has no dependencies.
|
A target without a @racket[deps] clause has no dependencies.
|
||||||
|
|
||||||
@defform[(deps dependency ...)]{
|
@defform[(deps dependency ...)]{
|
||||||
Specifies target dependencies. The form is valid only as the dependency clause of @racket[target].
|
Specifies target dependencies. Each dependency is an ordinary Racket expression. The form is valid only as the dependency clause of @racket[target].
|
||||||
}
|
}
|
||||||
|
|
||||||
@defform[(phony name ...)]{
|
@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.
|
Marks the values produced by @racket[name ...] as phony for the surrounding makefile prefix. A value may also be a list of target names. A phony target is always executed when requested or reached as a dependency.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defform[(default-target name)]{
|
@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.
|
Selects the target value produced by @racket[name] for @racket[(make)] in the surrounding makefile prefix. If no default is specified, the first registered 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{Generated targets}
|
||||||
|
|
||||||
|
Because @racket[makefile] is a lexical context instead of a fixed list of clauses, targets can be generated by ordinary Racket code:
|
||||||
|
|
||||||
|
@racketblock[
|
||||||
|
(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 a separate target procedure for each object file. Each procedure closes over the corresponding @racket[src] and @racket[obj] values. The quoted static targets still define the normal bindings @racket[makefile-target-stuff-all] and @racket[makefile-target-stuff-clean].
|
||||||
|
|
||||||
@section{Executing targets}
|
@section{Executing targets}
|
||||||
|
|
||||||
@@ -81,7 +111,7 @@ Builds the supplied targets in the makefile selected by @racket[current-makefile
|
|||||||
|
|
||||||
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.
|
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.
|
For a target that needs rebuilding, the engine looks up the registered target procedure for the active prefix and calls it.
|
||||||
}
|
}
|
||||||
|
|
||||||
@defthing[current-makefile-prefix parameter?]{
|
@defthing[current-makefile-prefix parameter?]{
|
||||||
@@ -95,8 +125,22 @@ Another registered makefile can be selected explicitly:
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@defthing[makefile-targets hash?]{
|
@section{Inspecting registered makefiles}
|
||||||
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].
|
|
||||||
|
@defproc[(makefile-prefixes) list?]{
|
||||||
|
Returns the registered makefile prefixes in evaluation order. The original prefix values are preserved.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(makefile-targets [prefix (or/c symbol? path-string?) (current-makefile-prefix)]) list?]{
|
||||||
|
Returns the targets registered for @racket[prefix] in registration order. The original target values are preserved, so symbolic targets are returned as symbols and dynamically generated path targets are returned as paths.
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(makefile-target-exists? [name (or/c symbol? path-string?)]) boolean?]{
|
||||||
|
Returns whether @racket[name] exists in the active makefile. To inspect another prefix, use @racket[(makefile-target-exists? prefix name)].
|
||||||
|
}
|
||||||
|
|
||||||
|
@defproc[(makefile-target-procedure [name (or/c symbol? path-string?)]) procedure?]{
|
||||||
|
Returns the registered target procedure for @racket[name] in the active makefile. To inspect another prefix, use @racket[(makefile-target-procedure prefix name)]. An unknown target raises an exception.
|
||||||
}
|
}
|
||||||
|
|
||||||
@section{Rash integration}
|
@section{Rash integration}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
#lang racket
|
||||||
|
|
||||||
|
(require rackunit
|
||||||
|
racket/file
|
||||||
|
racket-makefile
|
||||||
|
(only-in "../private/engine.rkt" reset-makefiles!))
|
||||||
|
|
||||||
|
(reset-makefiles!)
|
||||||
|
|
||||||
|
(define tmp (make-temporary-file "racket-makefile-generated~a" 'directory))
|
||||||
|
(define called '())
|
||||||
|
|
||||||
|
(dynamic-wind
|
||||||
|
void
|
||||||
|
(λ ()
|
||||||
|
(parameterize ([current-directory tmp])
|
||||||
|
(makefile 'stuff
|
||||||
|
(define sources '("foo.c" "bar.c" "baz.c"))
|
||||||
|
|
||||||
|
(for ([src sources])
|
||||||
|
(call-with-output-file src
|
||||||
|
#:exists 'truncate/replace
|
||||||
|
(λ (out)
|
||||||
|
(displayln src out))))
|
||||||
|
|
||||||
|
(define objects
|
||||||
|
(for/list ([src sources])
|
||||||
|
(define obj (path-replace-extension src #".o"))
|
||||||
|
(target obj
|
||||||
|
(deps src)
|
||||||
|
(set! called (append called (list (list $< $target))))
|
||||||
|
(copy-file $< $target #t))
|
||||||
|
obj))
|
||||||
|
|
||||||
|
(default-target 'all)
|
||||||
|
(phony 'all 'clean)
|
||||||
|
|
||||||
|
(target 'all
|
||||||
|
(deps objects))
|
||||||
|
|
||||||
|
(target 'clean
|
||||||
|
(apply rm-f objects)))
|
||||||
|
|
||||||
|
;; Definitions inside makefile remain ordinary module definitions.
|
||||||
|
(check-equal? sources '("foo.c" "bar.c" "baz.c"))
|
||||||
|
(check-equal? (map path->string objects) '("foo.o" "bar.o" "baz.o"))
|
||||||
|
|
||||||
|
(check-equal? (current-makefile-prefix) 'stuff)
|
||||||
|
(check-true (procedure? makefile-target-stuff-all))
|
||||||
|
(check-true (procedure? makefile-target-stuff-clean))
|
||||||
|
|
||||||
|
(check-equal?
|
||||||
|
(makefile-targets 'stuff)
|
||||||
|
(append objects '(all clean)))
|
||||||
|
|
||||||
|
(for ([obj objects])
|
||||||
|
(check-true
|
||||||
|
(procedure?
|
||||||
|
(makefile-target-procedure 'stuff obj))))
|
||||||
|
|
||||||
|
(make 'all)
|
||||||
|
|
||||||
|
(check-equal?
|
||||||
|
called
|
||||||
|
'(("foo.c" "foo.o")
|
||||||
|
("bar.c" "bar.o")
|
||||||
|
("baz.c" "baz.o")))
|
||||||
|
|
||||||
|
(for ([obj objects])
|
||||||
|
(check-true (file-exists? obj)))
|
||||||
|
|
||||||
|
(make 'clean)
|
||||||
|
|
||||||
|
(for ([obj objects])
|
||||||
|
(check-false (file-exists? obj)))))
|
||||||
|
(λ ()
|
||||||
|
(delete-directory/files tmp #:must-exist? #f)))
|
||||||
+27
-15
@@ -9,19 +9,19 @@
|
|||||||
(define calls '())
|
(define calls '())
|
||||||
(define context #f)
|
(define context #f)
|
||||||
|
|
||||||
(makefile first
|
(makefile 'first
|
||||||
(default-target status)
|
(default-target 'status)
|
||||||
(phony status all context)
|
(phony 'status 'all 'context)
|
||||||
|
|
||||||
(target status
|
(target 'status
|
||||||
(set! calls (append calls '(first-status))))
|
(set! calls (append calls '(first-status))))
|
||||||
|
|
||||||
(target all
|
(target 'all
|
||||||
(deps status)
|
(deps 'status)
|
||||||
(set! calls (append calls '(first-all))))
|
(set! calls (append calls '(first-all))))
|
||||||
|
|
||||||
(target context
|
(target 'context
|
||||||
(deps input.txt other.txt)
|
(deps 'input.txt 'other.txt)
|
||||||
(set! context (list $target $deps $<))))
|
(set! context (list $target $deps $<))))
|
||||||
|
|
||||||
(check-equal? (current-makefile-prefix) 'first)
|
(check-equal? (current-makefile-prefix) 'first)
|
||||||
@@ -30,6 +30,14 @@
|
|||||||
(check-true (procedure? makefile-target-first-all))
|
(check-true (procedure? makefile-target-first-all))
|
||||||
(check-true (procedure? makefile-target-first-context))
|
(check-true (procedure? makefile-target-first-context))
|
||||||
|
|
||||||
|
;; The public inspection API preserves definition order and original values.
|
||||||
|
(check-equal? (makefile-prefixes) '(first))
|
||||||
|
(check-equal? (makefile-targets) '(status all context))
|
||||||
|
(check-true (makefile-target-exists? 'status))
|
||||||
|
(check-false (makefile-target-exists? 'missing))
|
||||||
|
(check-eq? (makefile-target-procedure 'status)
|
||||||
|
makefile-target-first-status)
|
||||||
|
|
||||||
;; make resolves the target procedure through the active prefix.
|
;; make resolves the target procedure through the active prefix.
|
||||||
(make 'all)
|
(make 'all)
|
||||||
(check-equal? calls '(first-status first-all))
|
(check-equal? calls '(first-status first-all))
|
||||||
@@ -42,10 +50,10 @@
|
|||||||
(makefile-target-first-context)
|
(makefile-target-first-context)
|
||||||
(check-equal? context '("context" ("input.txt" "other.txt") "input.txt"))
|
(check-equal? context '("context" ("input.txt" "other.txt") "input.txt"))
|
||||||
|
|
||||||
(makefile second
|
(makefile 'second
|
||||||
(default-target status)
|
(default-target 'status)
|
||||||
(phony status)
|
(phony 'status)
|
||||||
(target status
|
(target 'status
|
||||||
(set! calls (append calls '(second-status)))))
|
(set! calls (append calls '(second-status)))))
|
||||||
|
|
||||||
;; The last makefile definition becomes active.
|
;; The last makefile definition becomes active.
|
||||||
@@ -60,7 +68,11 @@
|
|||||||
calls
|
calls
|
||||||
'(first-status first-all first-status second-status first-status))
|
'(first-status first-all first-status second-status first-status))
|
||||||
|
|
||||||
;; The registry stores the generated procedure itself.
|
;; Both prefixes remain inspectable after switching the active makefile.
|
||||||
(check-eq?
|
(check-equal? (makefile-prefixes) '(first second))
|
||||||
(hash-ref makefile-targets '("first" . "status"))
|
(check-equal? (makefile-targets 'first) '(status all context))
|
||||||
|
(check-equal? (makefile-targets 'second) '(status))
|
||||||
|
(check-true (makefile-target-exists? 'first 'status))
|
||||||
|
(check-false (makefile-target-exists? 'first 'missing))
|
||||||
|
(check-eq? (makefile-target-procedure 'first 'status)
|
||||||
makefile-target-first-status)
|
makefile-target-first-status)
|
||||||
|
|||||||
+7
-7
@@ -31,18 +31,18 @@
|
|||||||
(build-path tmp "Makefile.rkt")
|
(build-path tmp "Makefile.rkt")
|
||||||
"#lang racket"
|
"#lang racket"
|
||||||
"(require racket-makefile \"slow.rkt\")"
|
"(require racket-makefile \"slow.rkt\")"
|
||||||
"(makefile demo"
|
"(makefile 'demo"
|
||||||
" (phony change old)"
|
" (phony 'change 'old)"
|
||||||
" (target old (displayln \"old\"))"
|
" (target 'old (displayln \"old\"))"
|
||||||
" (target change (copy-file \"NewMakefile.rkt\" \"Makefile.rkt\" #t)))")
|
" (target 'change (copy-file \"NewMakefile.rkt\" \"Makefile.rkt\" #t)))")
|
||||||
|
|
||||||
(write-lines
|
(write-lines
|
||||||
(build-path tmp "NewMakefile.rkt")
|
(build-path tmp "NewMakefile.rkt")
|
||||||
"#lang racket"
|
"#lang racket"
|
||||||
"(require racket-makefile \"slow.rkt\")"
|
"(require racket-makefile \"slow.rkt\")"
|
||||||
"(makefile demo"
|
"(makefile 'demo"
|
||||||
" (phony changed)"
|
" (phony 'changed)"
|
||||||
" (target changed"
|
" (target 'changed"
|
||||||
" (call-with-output-file \"result.txt\" #:exists 'truncate/replace"
|
" (call-with-output-file \"result.txt\" #:exists 'truncate/replace"
|
||||||
" (lambda (out) (display slow-value out)))))")
|
" (lambda (out) (display slow-value out)))))")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user