(module make-unit mzscheme (require (lib "unitsig.ss")) (require "make-sig.ss") (provide make@) (define make@ (unit/sig make^ (import) (define-struct (exn:fail:make exn:fail) (target orig-exn)) (define make-print-checking (make-parameter #t)) (define make-print-dep-no-line (make-parameter #t)) (define make-print-reasons (make-parameter #t)) (define make-notify-handler (make-parameter void)) ;(define-type line (list (union path-string (list-of path-string)) (list-of path-string) thunk)) ;(define-type spec (list-of line)) (define (path-string=? a b) (equal? (if (string? a) (string->path a) a) (if (string? b) (string->path b) b))) (define (path-string->string s) (if (string? s) s (path->string s))) ; find-matching-line : path-string spec -> (union line #f) (define (find-matching-line str spec) (let ([match? (lambda (s) (path-string=? s str))]) (let loop ([lines spec]) (cond [(null? lines) #f] [else (let* ([line (car lines)] [names (if (path-string? (car line)) (list (car line)) (car line))]) (if (ormap match? names) line (loop (cdr lines))))])))) ; form-error : TST TST -> a (define (form-error s p) (error 'make/proc "~a: ~s" s p)) ; line-error : TST TST TST -> a (define (line-error s p n) (error 'make/proc "~a: ~s for line: ~a" s p n)) ; check-spec : TST -> #t ; effect : calls error if input is not a spec (define (check-spec spec) (and (or (list? spec) (form-error "specification is not a list" spec)) (or (pair? spec) (form-error "specification is an empty list" spec)) (andmap (lambda (line) (and (or (and (list? line) (<= 2 (length line) 3)) (form-error "list is not a list with 2 or 3 parts" line)) (or (or (path-string? (car line)) (and (list? (car line)) (andmap path-string? (car line)))) (form-error "line does not start with a path/string or list of paths/strings" line)) (let ([name (car line)]) (or (list? (cadr line)) (line-error "second part of line is not a list" (cadr line) name) (andmap (lambda (dep) (or (path-string? dep) (form-error "dependency item is not a path/string" dep name))) (cadr line))) (or (null? (cddr line)) (and (procedure? (caddr line)) (procedure-arity-includes? (caddr line) 0)) (line-error "command part of line is not a thunk" (caddr line) name))))) spec))) ; check-spec : TST -> #t ; effect : calls error if input is not a (union path-string (vector-of path-string)) (define (check-argv argv) (or (path-string? argv) (and (vector? argv) (andmap path-string? (vector->list argv))) (raise-type-error 'make/proc "path/string or path/string vector" argv))) ; make/proc/helper : spec (union path-string (vector-of path-string)) -> void ; effect : make, according to spec and argv. See doc.txt for details (define (make/proc/helper spec argv) (check-spec spec) (check-argv argv) (letrec ([made null] [make-file (lambda (s indent) (let ([line (find-matching-line s spec)] [date (and (or (directory-exists? s) (file-exists? s)) (file-or-directory-modify-seconds s))]) (when (and (make-print-checking) (or line (make-print-dep-no-line))) (printf "make: ~achecking ~a~n" indent s)) (if line (let ([deps (cadr line)]) (for-each (let ([new-indent (string-append " " indent)]) (lambda (d) (make-file d new-indent))) deps) (let ([reason (or (not date) (ormap (lambda (dep) (unless (or (file-exists? dep) (directory-exists? dep)) (error 'make "dependancy ~a was not made~n" dep)) (and (> (file-or-directory-modify-seconds dep) date) dep)) deps))]) (when reason (let ([l (cddr line)]) (unless (null? l) (set! made (cons s made)) ((make-notify-handler) s) (printf "make: ~amaking ~a~a~n" (if (make-print-checking) indent "") (path-string->string s) (if (make-print-reasons) (cond [(not date) (string-append " because " (path-string->string s) " does not exist")] [(path-string? reason) (string-append " because " (path-string->string reason) " changed")] [else (string-append (format " because (reason: ~a date: ~a)" reason date))]) "")) (with-handlers ([exn:fail? (lambda (exn) (raise (make-exn:fail:make (string->immutable-string (format "make: Failed to make ~a; ~a" (let ([fst (car line)]) (if (pair? fst) (map path-string->string fst) (path-string->string fst))) (if (exn? exn) (exn-message exn) exn))) (if (exn? exn) (exn-continuation-marks exn) (current-continuation-marks)) (car line) exn)))]) ((car l)))))))) (unless date (error 'make "don't know how to make ~a" (path-string->string s))))))]) (cond [(path-string? argv) (make-file argv "")] [(equal? argv #()) (make-file (caar spec) "")] [else (for-each (lambda (f) (make-file f "")) (vector->list argv))]) (for-each (lambda (item) (printf "make: made ~a~n" (path-string->string item))) (reverse made)))) (define make/proc (case-lambda [(spec) (make/proc/helper spec #())] [(spec argv) (make/proc/helper spec argv)])))))