brought pollen/lang/pre up to date + regeneration of preprocessor files

pull/9/head
Matthew Butterick 11 years ago
parent 35cee26d69
commit 783b2fb9fe

@ -9,9 +9,10 @@
(case arg (case arg
[("serve") `(require "server.rkt")] [("serve") `(require "server.rkt")]
[("regenerate") `(begin [("regenerate") `(begin
;; todo: take extensions off the comand line
(displayln "Regenerate preproc & pmap files ...") (displayln "Regenerate preproc & pmap files ...")
(require "regenerate.rkt" "pollen-file-tools.rkt") (require "regenerate.rkt" "pollen-file-tools.rkt")
(map regenerate (append-map project-files-with-ext (list POLLEN_PREPROC_EXT POLLEN_MAP_EXT))))] (map force-regenerate (append-map project-files-with-ext (list POLLEN_PREPROC_EXT POLLEN_MAP_EXT))))]
[("clone") (let ([target-path [("clone") (let ([target-path
(if (> (len args) 1) (if (> (len args) 1)
(->path (get args 1)) (->path (get args 1))

@ -1,5 +1,8 @@
#lang racket/base #lang racket/base
(require (only-in (planet mb/pollen/tools) as-list trim-whitespace))
(require (only-in (planet mb/pollen/readability) ->list)
(only-in (planet mb/pollen/decode) trim)
(only-in (planet mb/pollen/predicates) whitespace?))
(provide (except-out (all-from-out racket/base) #%module-begin) (provide (except-out (all-from-out racket/base) #%module-begin)
(rename-out [module-begin #%module-begin])) (rename-out [module-begin #%module-begin]))
@ -16,17 +19,15 @@
; doclang2_raw is a clone of scribble/doclang2 with decode disabled ; doclang2_raw is a clone of scribble/doclang2 with decode disabled
; helpful because it collects & exports content via 'doc ; helpful because it collects & exports content via 'doc
(module pollen-inner (planet mb/pollen/doclang2_raw) (module pollen-inner (planet mb/pollen/lang/doclang2_raw)
(require (planet mb/pollen/tools) (require (planet mb/pollen/tools) (planet mb/pollen/main-helper))
web-server/templates ; for subtemplating (require-extras #:provide #t) ; brings in the project require files
(planet mb/pollen/main-helper)) ; for split-metas and get-here
(require-and-provide-extras) ; brings in the project require files
expr ...) ; body of module expr ...) ; body of module
(require 'pollen-inner) ; provides 'doc (require 'pollen-inner) ; provides 'doc
(define text (trim-whitespace (as-list doc))) ; if single line, text will be a string (define text (trim (->list doc) whitespace?)) ; if single line, text will be a string
(provide text (all-from-out 'pollen-inner)) (provide text (all-from-out 'pollen-inner))

@ -96,7 +96,7 @@
(define/contract (has-preproc-source? x) (define/contract (has-preproc-source? x)
(any/c . -> . boolean?) (any/c . -> . boolean?)
(file-exists? (make-preproc-in-path (->path x)))) (file-exists? (make-preproc-source-path (->path x))))
(define/contract (has-pollen-source? x) (define/contract (has-pollen-source? x)
(any/c . -> . boolean?) (any/c . -> . boolean?)
@ -121,13 +121,21 @@
(any/c . -> . boolean?) (any/c . -> . boolean?)
(has-ext? (->path x) POLLEN_SOURCE_EXT)) (has-ext? (->path x) POLLEN_SOURCE_EXT))
;; this is for regenerate module.
;; when we want to be friendly with inputs for functions that require a path.
;; Strings & symbols often result from xexpr parsing
;; and are trivially converted to paths.
;; so let's say close enough.
(define/contract (pathish? x)
(any/c . -> . boolean?)
(->boolean (or path? string? symbol?)))
(define/contract (make-preproc-in-path path) (define/contract (make-preproc-source-path path)
(path? . -> . path?) (path? . -> . path?)
(add-ext path POLLEN_PREPROC_EXT)) (add-ext path POLLEN_PREPROC_EXT))
(define/contract (make-preproc-out-path path) (define/contract (make-preproc-output-path path)
(path? . -> . path?) (path? . -> . path?)
(remove-ext path)) (remove-ext path))

@ -39,7 +39,7 @@
;; general way of coercing to path ;; general way of coercing to path
(define (->path thing) (define (->path thing)
; todo: on bad input, it will pop a string error rather than symbol error ; todo: on bad input, it will pop a string error rather than path error
(string->path (->string thing))) (string->path (->string thing)))
(define (->complete-path thing) (define (->complete-path thing)

@ -5,6 +5,8 @@
(module+ test (require rackunit)) (module+ test (require rackunit))
(provide regenerate force-regenerate)
;; mod-dates is a hash that takes lists of paths as keys, ;; mod-dates is a hash that takes lists of paths as keys,
;; and lists of modification times as values. ;; and lists of modification times as values.
;; Reason: a templated page is a combination of two source files. ;; Reason: a templated page is a combination of two source files.
@ -30,8 +32,9 @@
;; put list of paths into mod-dates ;; put list of paths into mod-dates
;; want to take list as input (rather than individual path) ;; want to take list as input (rather than individual path)
;; because hash key needs to be a list ;; because hash key needs to be a list
(define/contract (store-refresh-in-mod-dates paths) (define/contract (store-refresh-in-mod-dates path-or-paths)
((listof path?) . -> . void?) ((or/c path? (listof path?)) . -> . void?)
(define paths (->list path-or-paths))
(hash-set! mod-dates paths (map path->mod-date-value paths))) (hash-set! mod-dates paths (map path->mod-date-value paths)))
(module+ test (module+ test
@ -54,10 +57,11 @@
(check-true (= (len mod-dates) 0))) (check-true (= (len mod-dates) 0)))
;; how to know whether a certain combination of paths needs a refresh ;; how to know whether a certain combination of paths needs a refresh
(define/contract (source-needs-refresh? paths) (define/contract (source-needs-refresh? path-or-paths)
((listof path?) . -> . boolean?) ((or/c path? (listof path?)) . -> . boolean?)
(let ([paths (->list path-or-paths)])
(or (not (paths . in? . mod-dates)) ; no stored mod date (or (not (paths . in? . mod-dates)) ; no stored mod date
(not (equal? (map path->mod-date-value paths) (get mod-dates paths))))) ; data has changed (not (equal? (map path->mod-date-value paths) (get mod-dates paths)))))) ; data has changed
(module+ test (module+ test
(reset-mod-dates) (reset-mod-dates)
@ -68,62 +72,87 @@
(check-true (source-needs-refresh? (list path))))) (check-true (source-needs-refresh? (list path)))))
; helper functions for regenerate functions ;; convenience function for external modules to use
;(define pollen-file-root (current-directory)) (define/contract (force-regenerate x)
(pathish? . -> . void?)
; complete pollen path = (regenerate x #:force #t))
;(build-path pollen-file-root f)
;; regenerate files listed in a pmap file
(define/contract (regenerate-with-pmap pmap)
(pmap? . -> . void?)
(for-each regenerate (all-pages pmap)))
;; todo: write test
;; dispatches path to the right regeneration function
;; dispatches path to the right place ;; use #:force to refresh regardless of cached state
(define/contract (regenerate path #:force [force #f]) (define/contract (regenerate x #:force [force #f])
(path? . -> . void?) ((pathish?) (#:force boolean?) . ->* . void?)
(regenerating-message path) (let ([path (->complete-path (->path x))])
(let ([path (->complete-path path)])
(cond (cond
;; this will catch pp (preprocessor) files
[(needs-preproc? path) (regenerate-with-preproc path #:force force)]
;; this will catch p files,
;; and files without extension that correspond to p files
[(needs-template? path) (regenerate-with-template path #:force force)]
;; this will catch pmap (pollen map) files
[(pmap-source? path) (let ([pmap (dynamic-require path 'main)]) [(pmap-source? path) (let ([pmap (dynamic-require path 'main)])
(regenerate-with-pmap pmap))] (regenerate-with-pmap pmap #:force force))]
[(needs-preproc? path) (do-preproc path #:force force)] [else (error "Regenerate: no handler for" (->string (file-name-from-path path)))])))
[(needs-template? path) (do-template path #:force force)])))
;; todo: write test
;; todo: write tests
;; todo: write contract & tests
(define (regenerating-message path) (define (regenerating-message path)
;; you can actually stuff whatever string you want into path —
;; if it's not really a path, file-name-from-path won't choke
(message "Regenerating:" (->string (file-name-from-path path)))) (message "Regenerating:" (->string (file-name-from-path path))))
(define (do-preproc path #:force [force #f]) ;; todo: write contract & tests
; set up preproc-in-path & preproc-out-path values (define (regenerated-message path)
(let-values (message "Regenerated:" (->string (file-name-from-path path))))
([(preproc-in-path preproc-out-path)
(if (preproc-source? path)
(values path (make-preproc-out-path path)) (define/contract (regenerate-with-preproc x #:force [force #f])
(values (make-preproc-in-path path) path))]) ((pathish?) (#:force boolean?) . ->* . void?)
(define path (->path x))
(when (and (file-exists? preproc-in-path) ;; path might be either a preproc-source path or preproc-output path
(or force ;; figure out which, then compute the other
(not (file-exists? preproc-out-path)) (define-values (source-path output-path) (if (preproc-source? path)
(source-needs-refresh? preproc-in-path))) (values path (make-preproc-output-path path))
(store-refresh-in-mod-dates preproc-in-path) (values (make-preproc-source-path path) path)))
; use single quotes to escape spaces in pathnames
(define command ;; Computing the source-path doesn't validate whether it exists.
(format "~a '~a' > '~a'" RACKET_PATH preproc-in-path preproc-out-path)) ;; Which is important, of course.
; discard output using open-output-nowhere (if (file-exists? source-path)
;; Three conditions under which we refresh:
(if (or
;; 1) explicitly forced refresh
force
;; 2) output file doesn't exist (so it definitely won't appear in mod-dates)
;; also, this is convenient for development:
;; you can trigger a refresh just by deleting the file
(not (file-exists? output-path))
;; 3) file otherwise needs refresh (e.g., it changed)
(source-needs-refresh? source-path))
;; use single quotes to escape spaces in pathnames
(let ([command (format "~a '~a' > '~a'" RACKET_PATH source-path output-path)])
(regenerating-message (format "~a from ~a"
(file-name-from-path output-path)
(file-name-from-path source-path)))
(store-refresh-in-mod-dates source-path)
;; discard output using open-output-nowhere
(parameterize ([current-output-port (open-output-nowhere)]) (parameterize ([current-output-port (open-output-nowhere)])
(system command)) (system command))
(regenerate-message preproc-out-path)))) (regenerated-message output-path))
;; otherwise, skip file because there's no trigger for refresh
(message "File is up to date:" (->string (file-name-from-path output-path))))
;; source-path doesn't exist
(message "Preprocessor file not found:" (->string (file-name-from-path source-path)))))
;; todo: write tests
(define (do-template path [template-name empty] #:force [force #f]) ;;;;;;;;;;;;;;;
;; todo next
;;;;;;;;;;;;;;;
(define (regenerate-with-template path [template-name empty] #:force [force #f])
; take full path or filename ; take full path or filename
; return full path of templated file ; return full path of templated file
@ -201,7 +230,14 @@
(display-to-file #:exists 'replace page-result generated-path) (display-to-file #:exists 'replace page-result generated-path)
(regenerate-message generated-path))) (regenerated-message generated-path)))
(provide regenerate regenerate-all-files) ;; regenerate files listed in a pmap file
(define/contract (regenerate-with-pmap pmap #:force [force #f])
(pmap? . -> . void?)
;; pass force parameter through
(for-each (λ(i) (regenerate i #:force force)) (all-pages pmap)))
;; todo: write test

@ -0,0 +1 @@
This will print bar: bar

@ -0,0 +1,5 @@
#lang planet mb/pollen/pre
(define foo "bar")
This will print bar: foo
Loading…
Cancel
Save