Removed rash integration again and backported raco call.

This commit is contained in:
2026-08-17 21:40:48 +02:00
parent 26a0e780c5
commit a21f4a4a39
15 changed files with 716 additions and 1287 deletions
+15 -11
View File
@@ -8,30 +8,34 @@
(dynamic-wind
void
(lambda ()
(λ ()
(parameterize ([current-directory tmp]
[current-command-line-arguments #()])
(call-with-output-file "input.txt"
(lambda (out) (display "one" out)))
(λ (out) (display "one" out)))
(define build-count 0)
(reset-makefile!)
(set-default-target! 'all)
(mark-phony! 'all)
(reset-makefiles!)
(begin-makefile! 'demo)
(set-default-target! 'demo 'all)
(mark-phony! 'demo 'all)
(register-target!
'demo
"result.txt"
'("input.txt")
(lambda ()
(λ ()
(set! build-count (add1 build-count))
(copy-file "input.txt" "result.txt" #t)))
;; A dependency expression may produce a list. The engine flattens it.
(register-target! 'all
(list (list "result.txt"))
void)
(register-target!
'demo
'all
(list (list "result.txt"))
void)
(current-makefile-prefix 'demo)
(make-targets!)
(check-equal? build-count 1)
(check-true (file-exists? "result.txt"))
@@ -45,5 +49,5 @@
(file-or-directory-modify-seconds "input.txt" (+ result-time 2))
(make-targets!)
(check-equal? build-count 2)))
(lambda ()
(λ ()
(delete-directory/files tmp #:must-exist? #f)))
+53 -42
View File
@@ -1,55 +1,66 @@
#lang racket
(require rackunit
racket/file
racket/runtime-path
racket/system)
racket-makefile
(only-in "../private/engine.rkt" reset-makefiles!))
(define-runtime-path package-root "..")
(reset-makefiles!)
(define racket-executable
(find-system-path 'exec-file))
(define calls '())
(define context #f)
(define tmp (make-temporary-file "racket-makefile-make~a" 'directory))
(makefile first
(default-target status)
(phony status all context)
(define (run-racket . args)
(parameterize ([current-directory tmp])
(apply system* racket-executable args)))
(target status
(set! calls (append calls '(first-status))))
(dynamic-wind
void
(lambda ()
(call-with-output-file (build-path tmp "Makefile.rkt")
(lambda (out)
(displayln "#lang racket-makefile" out)
(displayln "(default-target all)" out)
(displayln "(phony all clean 'compile)" out)
(displayln "(target all (call-with-output-file \"all.out\" (lambda (o) (display \"all\" o)) #:exists 'replace))" out)
(displayln "(target clean (rm-f \"all.out\" \"compile.out\"))" out)
;; `compile` is deliberately chosen because Racket already binds that
;; identifier. `(make compile)` must still mean the target named compile.
(displayln "(target 'compile (call-with-output-file \"compile.out\" (lambda (o) (display \"compile\" o)) #:exists 'replace))" out)))
(target all
(deps status)
(set! calls (append calls '(first-all))))
;; Loading the makefile alone must not run any target.
(check-true (run-racket "-t" "Makefile.rkt"))
(check-false (file-exists? (build-path tmp "all.out")))
(target context
(deps input.txt other.txt)
(set! context (list $target $deps $<))))
;; Explicit make invocation from the command line.
(check-true (run-racket "-t" "Makefile.rkt" "-e" "(make all)"))
(check-true (file-exists? (build-path tmp "all.out")))
(check-equal? (current-makefile-prefix) 'first)
(check-true (procedure? make))
(check-true (procedure? makefile-target-first-status))
(check-true (procedure? makefile-target-first-all))
(check-true (procedure? makefile-target-first-context))
;; A bare target identifier is literal, even when Racket binds the same name.
(check-true (run-racket "-t" "Makefile.rkt" "-e" "(make compile)"))
(check-true (file-exists? (build-path tmp "compile.out")))
;; make resolves the target procedure through the active prefix.
(make 'all)
(check-equal? calls '(first-status first-all))
;; Multiple targets are executed in the requested order.
(check-true (run-racket "-t" "Makefile.rkt" "-e" "(make clean all)"))
(check-true (file-exists? (build-path tmp "all.out")))
(check-false (file-exists? (build-path tmp "compile.out")))
;; The generated target is a real Racket procedure and can be called directly.
(makefile-target-first-status)
(check-equal? calls '(first-status first-all first-status))
;; (make) uses the configured default target.
(delete-file (build-path tmp "all.out"))
(check-true (run-racket "-t" "Makefile.rkt" "-e" "(make)"))
(check-true (file-exists? (build-path tmp "all.out"))))
(lambda ()
(delete-directory/files tmp #:must-exist? #f)))
;; Direct procedure calls still get the automatic target variables.
(makefile-target-first-context)
(check-equal? context '("context" ("input.txt" "other.txt") "input.txt"))
(makefile second
(default-target status)
(phony status)
(target status
(set! calls (append calls '(second-status)))))
;; The last makefile definition becomes active.
(check-equal? (current-makefile-prefix) 'second)
(make)
(check-equal? calls '(first-status first-all first-status second-status))
;; Both prefixes remain registered and can be selected explicitly.
(current-makefile-prefix 'first)
(make 'status)
(check-equal?
calls
'(first-status first-all first-status second-status first-status))
;; The registry stores the generated procedure itself.
(check-eq?
(hash-ref makefile-targets '("first" . "status"))
makefile-target-first-status)
+29 -91
View File
@@ -2,26 +2,21 @@
(require rackunit
racket/file
racket/runtime-path
racket/system)
(define racket-executable
(find-system-path 'exec-file))
(define racket-executable (find-system-path 'exec-file))
(define tmp (make-temporary-file "racket-makefile-refresh~a" 'directory))
(define (write-lines path . lines)
(call-with-output-file path
#:exists 'truncate/replace
(lambda (out)
(λ (out)
(for ([line (in-list lines)])
(displayln line out)))))
(dynamic-wind
void
(lambda ()
;; This module records how often it is instantiated. A refresh must reuse
;; the existing module instance instead of loading it a second time.
(λ ()
(write-lines
(build-path tmp "slow.rkt")
"#lang racket/base"
@@ -34,99 +29,42 @@
(write-lines
(build-path tmp "Makefile.rkt")
"#lang racket-makefile"
"(require \"slow.rkt\")"
"(phony change)"
"(target change (copy-file \"NewMakefile.rkt\" \"Makefile.rkt\" #t))")
"#lang racket"
"(require racket-makefile \"slow.rkt\")"
"(makefile demo"
" (phony change old)"
" (target old (displayln \"old\"))"
" (target change (copy-file \"NewMakefile.rkt\" \"Makefile.rkt\" #t)))")
(write-lines
(build-path tmp "NewMakefile.rkt")
"#lang racket-makefile"
"(require \"slow.rkt\")"
"(phony changed)"
"(target changed"
" (call-with-output-file \"result.txt\" #:exists 'truncate/replace"
" (lambda (out) (display slow-value out))))")
(parameterize ([current-directory tmp])
(check-true
(system* racket-executable
"-t" "Makefile.rkt"
"-e" "(make change)"
"-e" "(refresh-makefile)"
"-e" "(make changed)")))
(check-equal? (file->string (build-path tmp "result.txt")) "loaded")
(check-equal? (file->string (build-path tmp "slow-count.txt")) "1")
(check-equal?
(filter (lambda (path)
(regexp-match? #rx"^racket-makefile-refresh.*[.]rkt$"
(path->string (file-name-from-path path))))
(directory-list tmp #:build? #t))
'()))
(lambda ()
(delete-directory/files tmp #:must-exist? #f)))
;; A makefile can also use ordinary #lang racket and require main.rkt directly.
;; This is the style used by racket-makefile's own development Makefile. The
;; declaration macros must remember the source file in that case, because the
;; custom racket-makefile #%module-begin is not involved.
(define direct-tmp (make-temporary-file "racket-makefile-direct-refresh~a" 'directory))
(define-runtime-path package-main "../main.rkt")
(define-runtime-path package-commands "../private/commands.rkt")
(define-runtime-path package-engine "../private/engine.rkt")
(dynamic-wind
void
(lambda ()
(make-directory (build-path direct-tmp "private"))
(copy-file package-main (build-path direct-tmp "main.rkt"))
(copy-file package-commands (build-path direct-tmp "private" "commands.rkt"))
(copy-file package-engine (build-path direct-tmp "private" "engine.rkt"))
(write-lines
(build-path direct-tmp "slow.rkt")
"#lang racket/base"
"(require racket/file)"
"(define count-file \"slow-count.txt\")"
"(define count (if (file-exists? count-file) (string->number (file->string count-file)) 0))"
"(call-with-output-file count-file #:exists 'truncate/replace (lambda (out) (display (add1 count) out)))"
"(provide slow-value)"
"(define slow-value \"loaded\")")
(write-lines
(build-path direct-tmp "Makefile.rkt")
"#lang racket"
"(require \"main.rkt\" \"slow.rkt\")"
"(phony change old)"
"(target old (displayln \"old\"))"
"(target change (copy-file \"NewMakefile.rkt\" \"Makefile.rkt\" #t))")
"(require racket-makefile \"slow.rkt\")"
"(makefile demo"
" (phony changed)"
" (target changed"
" (call-with-output-file \"result.txt\" #:exists 'truncate/replace"
" (lambda (out) (display slow-value out)))))")
(write-lines
(build-path direct-tmp "NewMakefile.rkt")
(build-path tmp "driver.rkt")
"#lang racket"
"(require \"main.rkt\" \"slow.rkt\")"
"(phony changed)"
"(target changed"
" (call-with-output-file \"result.txt\" #:exists 'truncate/replace"
" (lambda (out) (display slow-value out))))")
(write-lines
(build-path direct-tmp "driver.rkt")
"#lang racket"
"(require \"main.rkt\")"
"(require racket-makefile)"
"(dynamic-require \"Makefile.rkt\" #f)"
"(make change)"
"(make 'change)"
"(refresh-makefile)"
"(make changed)"
"(with-handlers ([exn:fail? (lambda (e) (displayln \"old target removed\"))])"
" (make old)"
"(make 'changed)"
"(define old-removed?"
" (with-handlers ([exn:fail? (lambda (e) #t)])"
" (make 'old)"
" #f))"
"(unless old-removed?"
" (error 'refresh-test \"old target survived refresh\"))")
(parameterize ([current-directory direct-tmp])
(parameterize ([current-directory tmp])
(check-true (system* racket-executable "driver.rkt")))
(check-equal? (file->string (build-path direct-tmp "result.txt")) "loaded")
(check-equal? (file->string (build-path direct-tmp "slow-count.txt")) "1"))
(lambda ()
(delete-directory/files direct-tmp #:must-exist? #f)))
(check-equal? (file->string (build-path tmp "result.txt")) "loaded")
(check-equal? (file->string (build-path tmp "slow-count.txt")) "1"))
(λ ()
(delete-directory/files tmp #:must-exist? #f)))