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
[("serve") `(require "server.rkt")]
[("regenerate") `(begin
;; todo: take extensions off the comand line
(displayln "Regenerate preproc & pmap files ...")
(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
(if (> (len args) 1)
(->path (get args 1))

@ -1,5 +1,8 @@
#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)
(rename-out [module-begin #%module-begin]))
@ -16,17 +19,15 @@
; doclang2_raw is a clone of scribble/doclang2 with decode disabled
; helpful because it collects & exports content via 'doc
(module pollen-inner (planet mb/pollen/doclang2_raw)
(require (planet mb/pollen/tools)
web-server/templates ; for subtemplating
(planet mb/pollen/main-helper)) ; for split-metas and get-here
(require-and-provide-extras) ; brings in the project require files
(module pollen-inner (planet mb/pollen/lang/doclang2_raw)
(require (planet mb/pollen/tools) (planet mb/pollen/main-helper))
(require-extras #:provide #t) ; brings in the project require files
expr ...) ; body of module
(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))

@ -96,7 +96,7 @@
(define/contract (has-preproc-source? x)
(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)
(any/c . -> . boolean?)
@ -121,13 +121,21 @@
(any/c . -> . boolean?)
(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?)
(add-ext path POLLEN_PREPROC_EXT))
(define/contract (make-preproc-out-path path)
(define/contract (make-preproc-output-path path)
(path? . -> . path?)
(remove-ext path))

@ -39,7 +39,7 @@
;; general way of coercing to path
(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)))
(define (->complete-path thing)

@ -5,6 +5,8 @@
(module+ test (require rackunit))
(provide regenerate force-regenerate)
;; mod-dates is a hash that takes lists of paths as keys,
;; and lists of modification times as values.
;; Reason: a templated page is a combination of two source files.
@ -30,8 +32,9 @@
;; put list of paths into mod-dates
;; want to take list as input (rather than individual path)
;; because hash key needs to be a list
(define/contract (store-refresh-in-mod-dates paths)
((listof path?) . -> . void?)
(define/contract (store-refresh-in-mod-dates path-or-paths)
((or/c path? (listof path?)) . -> . void?)
(define paths (->list path-or-paths))
(hash-set! mod-dates paths (map path->mod-date-value paths)))
(module+ test
@ -54,10 +57,11 @@
(check-true (= (len mod-dates) 0)))
;; how to know whether a certain combination of paths needs a refresh
(define/contract (source-needs-refresh? paths)
((listof path?) . -> . boolean?)
(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
(define/contract (source-needs-refresh? path-or-paths)
((or/c path? (listof path?)) . -> . boolean?)
(let ([paths (->list path-or-paths)])
(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
(module+ test
(reset-mod-dates)
@ -68,62 +72,87 @@
(check-true (source-needs-refresh? (list path)))))
; helper functions for regenerate functions
;(define pollen-file-root (current-directory))
; complete pollen path =
;(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
;; convenience function for external modules to use
(define/contract (force-regenerate x)
(pathish? . -> . void?)
(regenerate x #:force #t))
;; dispatches path to the right place
(define/contract (regenerate path #:force [force #f])
(path? . -> . void?)
(regenerating-message path)
(let ([path (->complete-path path)])
;; dispatches path to the right regeneration function
;; use #:force to refresh regardless of cached state
(define/contract (regenerate x #:force [force #f])
((pathish?) (#:force boolean?) . ->* . void?)
(let ([path (->complete-path (->path x))])
(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)])
(regenerate-with-pmap pmap))]
[(needs-preproc? path) (do-preproc path #:force force)]
[(needs-template? path) (do-template path #:force force)])))
;; todo: write test
(regenerate-with-pmap pmap #:force force))]
[else (error "Regenerate: no handler for" (->string (file-name-from-path path)))])))
;; todo: write tests
;; todo: write contract & tests
(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))))
(define (do-preproc path #:force [force #f])
; set up preproc-in-path & preproc-out-path values
(let-values
([(preproc-in-path preproc-out-path)
(if (preproc-source? path)
(values path (make-preproc-out-path path))
(values (make-preproc-in-path path) path))])
(when (and (file-exists? preproc-in-path)
(or force
(not (file-exists? preproc-out-path))
(source-needs-refresh? preproc-in-path)))
(store-refresh-in-mod-dates preproc-in-path)
; use single quotes to escape spaces in pathnames
(define command
(format "~a '~a' > '~a'" RACKET_PATH preproc-in-path preproc-out-path))
; discard output using open-output-nowhere
(parameterize ([current-output-port (open-output-nowhere)])
(system command))
(regenerate-message preproc-out-path))))
(define (do-template path [template-name empty] #:force [force #f])
;; todo: write contract & tests
(define (regenerated-message path)
(message "Regenerated:" (->string (file-name-from-path path))))
(define/contract (regenerate-with-preproc x #:force [force #f])
((pathish?) (#:force boolean?) . ->* . void?)
(define path (->path x))
;; path might be either a preproc-source path or preproc-output path
;; figure out which, then compute the other
(define-values (source-path output-path) (if (preproc-source? path)
(values path (make-preproc-output-path path))
(values (make-preproc-source-path path) path)))
;; Computing the source-path doesn't validate whether it exists.
;; Which is important, of course.
(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)])
(system command))
(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
;;;;;;;;;;;;;;;
;; todo next
;;;;;;;;;;;;;;;
(define (regenerate-with-template path [template-name empty] #:force [force #f])
; take full path or filename
; return full path of templated file
@ -201,7 +230,14 @@
(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