diff --git a/collects/compiler/cm.rkt b/collects/compiler/cm.rkt index 61a598db06..99af78ff8c 100644 --- a/collects/compiler/cm.rkt +++ b/collects/compiler/cm.rkt @@ -17,7 +17,9 @@ manager-skip-file-handler file-stamp-in-collection file-stamp-in-paths - (rename-out [trace manager-trace-handler])) + (rename-out [trace manager-trace-handler]) + get-file-sha1 + get-compiled-file-sha1) (define manager-compile-notify-handler (make-parameter void)) (define trace (make-parameter void)) @@ -79,7 +81,7 @@ (or (not date) (zo-date . > . date))) (cons zo-date - (delay (get-compiled-sha1 mode (get-zo-path))))] + (delay (get-compiled-file-sha1 (get-zo-path) mode)))] [date (cons date (delay (get-source-sha1 (get-path))))] @@ -574,3 +576,11 @@ "empty use-compiled-file-paths list: " modes)) compilation-manager-load-handler)) + + +;; Exported: +(define (get-compiled-file-sha1 path) + (try-file-sha1 path (path-replace-suffix path #".dep"))) + +(define (get-file-sha1 path) + (get-source-sha1 path)) diff --git a/collects/scribblings/mzc/make.scrbl b/collects/scribblings/mzc/make.scrbl index 16d3e38b22..e43143f18b 100644 --- a/collects/scribblings/mzc/make.scrbl +++ b/collects/scribblings/mzc/make.scrbl @@ -82,12 +82,13 @@ would create only @filepath{compiled/b_scm.zo} and In addition to a bytecode file, @|mzc| creates a file @filepath{compiled/@nonterm{name}_@nonterm{ext}.dep} that records -dependencies of the compiled module on other module files. Using this -dependency information, a re-compilation request via @|mzc| can -consult both the source file's timestamp and the timestamps for the -sources and bytecode of imported modules. Furthermore, imported -modules are themselves compiled as necessary, including updating the -bytecode and dependency files for the imported modules, transitively. +dependencies of the compiled module on other module files and the +source file's SHA-1 hash. Using this dependency information, a +re-compilation request via @|mzc| can consult both the source file's +timestamp/hash and the timestamps/hashes for the bytecode of imported +modules. Furthermore, imported modules are themselves compiled as +necessary, including updating the bytecode and dependency files for +the imported modules, transitively. Continuing the @exec{mzc a.scm} example from the previous section, the @|mzc| creates @filepath{compiled/a_scm.dep}, @@ -96,7 +97,7 @@ the same time as the @filepath{.zo} files. The @filepath{compiled/a_scm.dep} file records the dependency of @filepath{a.scm} on @filepath{b.scm}, @filepath{c.scm} and the @schememodname[scheme] library. If the @filepath{b.scm} file is -modified (so that its timestamp changes), then running +modified (so that its timestamp and SHA-1 hash changes), then running @commandline{mzc a.scm} @@ -153,7 +154,9 @@ file if first sub-directory listed in @scheme[use-compiled-file-paths] (at the time that @scheme[make-compilation-manager-load/use-compiled-handler] - was called)} + was called), and either no @filepath{.dep} file exists or it + records a source-file SHA-1 hash that differs from the current + version and source-file SHA-1 hash;} @item{no @filepath{.dep} file exists next to the @filepath{.zo} file;} @@ -162,19 +165,28 @@ file if match the result of @scheme[(version)];} @item{one of the files listed in the @filepath{.dep} file has a - @filepath{.zo} timestamp newer than the one recorded in the - @filepath{.dep} file.} + @filepath{.zo} timestamp newer than the target @filepath{.zo}, + and the combined hashes of the dependencies recorded in the + @filepath{.dep} file does not match the combined hash recorded + in the @filepath{.dep} file.} ]} ] +If SHA-1 hashes override a timestamp-based decision to recompile the +file, then the target @filepath{.zo} file's timestamp is updated to +the current time. + After the handler procedure compiles a @filepath{.zo} file, it creates -a corresponding @filepath{.dep} file that lists the current version, -plus the @filepath{.zo} timestamp for every file that is +a corresponding @filepath{.dep} file that lists the current version +and the identification of every file that is directly @scheme[require]d by the module in the compiled file. Additional dependencies can be installed during compilation via -@schememodname[compiler/cm-accomplice]. +@schememodname[compiler/cm-accomplice]. The @filepath{.dep} file also +records the SHA-1 hash of the module's source, and it records a +combined SHA-1 hash of all of the dependencies that includes their +recursive dependencies. The handler caches timestamps when it checks @filepath{.dep} files, and the cache is maintained across calls to the same handler. The @@ -266,6 +278,21 @@ Returns the file-modification date and @scheme[delay]ed hash of This function is intended for use with @scheme[manager-skip-file-handler].} + +@defproc[(get-file-sha1 [p path?]) (or/c string? #f)]{ + +Computes a SHA-1 hash for the file @racket[p]; the result is +@racket[#f] if @racket[p] cannot be opened.} + + +@defproc[(get-compiled-file-sha1 [p path?]) (or/c string? #f)]{ + +Computes a SHA-1 hash for the bytecode file @racket[p], appending any +dependency-describing hash available from a @filepath{.dep} file when +available (i.e., the suffix on @racket[p] is replaced by +@filepath{.dep} to locate dependency information). The result is +@racket[#f] if @racket[p] cannot be opened.} + @; ---------------------------------------------------------------------- @section{Compilation Manager Hook for Syntax Transformers} diff --git a/collects/scribblings/scribble/scribble.scrbl b/collects/scribblings/scribble/scribble.scrbl index 3df63292be..79e74e757d 100644 --- a/collects/scribblings/scribble/scribble.scrbl +++ b/collects/scribblings/scribble/scribble.scrbl @@ -2,7 +2,7 @@ @(require scribble/bnf "utils.ss") -@title{@bold{Scribble}: PLT Documentation Tool} +@title{@bold{Scribble}: Racket Documentation Tool} @author["Matthew Flatt" "Eli Barzilay"] diff --git a/collects/setup/scribble.rkt b/collects/setup/scribble.rkt index 6f2baa92b3..0520d874ae 100644 --- a/collects/setup/scribble.rkt +++ b/collects/setup/scribble.rkt @@ -10,6 +10,7 @@ scheme/file scheme/fasl scheme/serialize + compiler/cm syntax/modread scribble/base-render scribble/core @@ -379,37 +380,60 @@ (define (make-sci-computed sci) (lambda () sci)) +(define (file-or-directory-modify-seconds/stamp file + stamp-time stamp-data pos + get-sha1) + (let ([t (file-or-directory-modify-seconds file #f (lambda () +inf.0))]) + (cond + [(t . <= . stamp-time) stamp-time] + [(equal? (list-ref stamp-data pos) (get-sha1 file)) stamp-time] + [else t]))) + (define ((get-doc-info only-dirs latex-dest auto-main? auto-user? with-record-error setup-printf) doc) (let* ([info-out-file (build-path (or latex-dest (doc-dest-dir doc)) "out.sxref")] [info-in-file (build-path (or latex-dest (doc-dest-dir doc)) "in.sxref")] + [stamp-file (build-path (or latex-dest (doc-dest-dir doc)) "stamp.sxref")] [out-file (build-path (doc-dest-dir doc) "index.html")] [src-zo (let-values ([(base name dir?) (split-path (doc-src-file doc))]) (build-path base "compiled" (path-add-suffix name ".zo")))] [renderer (make-renderer latex-dest doc)] [can-run? (can-build? only-dirs doc)] - [aux-time (max (file-or-directory-modify-seconds - (build-path (collection-path "scribble") - "compiled" - (path-add-suffix - (if latex-dest - "latex-render.ss" - "html-render.ss") - ".zo")) - #f (lambda () -inf.0)) - (file-or-directory-modify-seconds - (build-path (collection-path "scribble") - "scribble.css") - #f (lambda () +inf.0)))] + [stamp-time (file-or-directory-modify-seconds stamp-file #f (lambda () -inf.0))] + [stamp-data (with-handlers ([exn:fail:filesystem? (lambda (exn) (list "" "" ""))]) + (let ([v (call-with-input-file* stamp-file read)]) + (if (and (list? v) + (= 3 (length v)) + (andmap string? v)) + v + (list "" "" ""))))] + [renderer-path (build-path (collection-path "scribble") + "compiled" + (path-add-suffix + (if latex-dest + "latex-render.rkt" + "html-render.rkt") + ".zo"))] + [css-path (build-path (collection-path "scribble") + "scribble.css")] + [aux-time (max (file-or-directory-modify-seconds/stamp + renderer-path + stamp-time stamp-data 1 + get-compiled-file-sha1) + (file-or-directory-modify-seconds/stamp + css-path + stamp-time stamp-data 2 + get-file-sha1))] [my-time (file-or-directory-modify-seconds out-file #f (lambda () -inf.0))] [info-out-time (file-or-directory-modify-seconds info-out-file #f (lambda () #f))] [info-in-time (file-or-directory-modify-seconds info-in-file #f (lambda () #f))] [info-time (min (or info-out-time -inf.0) (or info-in-time -inf.0))] [vers (send renderer get-serialize-version)] - [src-time (max aux-time - (file-or-directory-modify-seconds - src-zo #f (lambda () +inf.0)))] + [src-time (file-or-directory-modify-seconds/stamp + src-zo + stamp-time stamp-data 0 + get-compiled-file-sha1)] [up-to-date? (and info-out-time info-in-time @@ -535,6 +559,13 @@ (unless latex-dest (render-time "xref-in" (write-in info))) (set-info-need-in-write?! info #f)) + (when (or (stamp-time . < . aux-time) + (stamp-time . < . src-time)) + (let ([data (list (get-compiled-file-sha1 src-zo) + (get-compiled-file-sha1 renderer-path) + (get-file-sha1 css-path))]) + (with-output-to-file stamp-file #:exists 'truncate/replace (lambda () (write data))) + (file-or-directory-modify-seconds stamp-file (max aux-time src-time)))) info)))) (lambda () #f)) #f))))