Files
racket-makefile/private/engine.rkt
T

149 lines
4.6 KiB
Racket

#lang racket/base
(require racket/list)
(provide register-target!
mark-phony!
set-default-target!
reset-makefile!
make-targets!
current-target
current-dependencies
current-first-dependency)
(struct target-definition (name dependencies recipe) #:transparent)
(define targets (make-hash))
(define phony-targets (make-hash))
(define target-order '())
(define default-target-name #f)
(define current-target (make-parameter #f))
(define current-dependencies (make-parameter #f))
(define current-first-dependency (make-parameter #f))
(define (target-key value)
(cond
[(symbol? value) (symbol->string value)]
[(path? value) (path->string value)]
[(string? value) value]
[else (raise-argument-error 'target "(or/c symbol? path-string?)" value)]))
(define (reset-makefile!)
(hash-clear! targets)
(hash-clear! phony-targets)
(set! target-order '())
(set! default-target-name #f))
(define (target-keys value)
(if (list? value)
(append-map target-keys value)
(list (target-key value))))
(define (register-target! name dependencies recipe)
(define key (target-key name))
(unless (hash-has-key? targets key)
(set! target-order (append target-order (list key))))
(hash-set! targets
key
(target-definition key
(append-map target-keys dependencies)
recipe))
(void))
(define (mark-phony! . names)
(for* ([name (in-list names)]
[key (in-list (target-keys name))])
(hash-set! phony-targets key #t))
(void))
(define (set-default-target! name)
(set! default-target-name (target-key name))
(void))
(define (phony? name)
(hash-ref phony-targets name #f))
(define (path-modify-seconds path)
(file-or-directory-modify-seconds path #f (lambda () #f)))
(define (dependency-time name built-results)
(cond
[(phony? name) +inf.0]
[(hash-has-key? targets name)
(define result (hash-ref built-results name #f))
(or (path-modify-seconds name)
(and result +inf.0)
#f)]
[else
(or (path-modify-seconds name)
(error 'racket-makefile "dependency does not exist and has no target: ~a" name))]))
(define (needs-build? definition built-results)
(define name (target-definition-name definition))
(cond
[(phony? name) #t]
[else
(define target-time (path-modify-seconds name))
(cond
[(not target-time) #t]
[else
(for/or ([dependency (in-list (target-definition-dependencies definition))])
(define dep-time (dependency-time dependency built-results))
(and dep-time (> dep-time target-time)))])]))
(define (execute-target! definition)
(define name (target-definition-name definition))
(define dependencies (target-definition-dependencies definition))
(printf "racket-makefile: ~a\n" name)
(parameterize ([current-target name]
[current-dependencies dependencies]
[current-first-dependency (and (pair? dependencies) (car dependencies))])
((target-definition-recipe definition))))
(define (build-target! name built-results visiting)
(define key (target-key name))
(cond
[(hash-has-key? built-results key)
(hash-ref built-results key)]
[(hash-has-key? visiting key)
(error 'racket-makefile "dependency cycle involving target: ~a" key)]
[else
(define definition
(hash-ref targets key
(lambda ()
(error 'racket-makefile "unknown target: ~a" key))))
(hash-set! visiting key #t)
(for ([dependency (in-list (target-definition-dependencies definition))])
(if (hash-has-key? targets dependency)
(build-target! dependency built-results visiting)
(unless (path-modify-seconds dependency)
(error 'racket-makefile
"dependency does not exist and has no target: ~a"
dependency))))
(define rebuilt? (needs-build? definition built-results))
(when rebuilt?
(execute-target! definition))
(hash-remove! visiting key)
(hash-set! built-results key rebuilt?)
rebuilt?]))
(define (default-targets)
(cond
[default-target-name (list default-target-name)]
[(pair? target-order) (list (car target-order))]
[else '()]))
(define (make-targets! . names)
(define selected
(if (null? names)
(default-targets)
(append-map target-keys names)))
(when (null? selected)
(error 'racket-makefile "no target defined"))
(define built-results (make-hash))
(define visiting (make-hash))
(for ([name (in-list selected)])
(build-target! name built-results visiting))
(void))