diff --git a/collects/racket/enter.rkt b/collects/racket/enter.rkt index 12d333a5e1..dc007feb3c 100644 --- a/collects/racket/enter.rkt +++ b/collects/racket/enter.rkt @@ -1,6 +1,8 @@ #lang racket/base -(require syntax/modcode racket/list (for-syntax racket/base)) +(require "rerequire.rkt" + racket/list + (for-syntax racket/base)) (provide enter!) @@ -38,7 +40,13 @@ (let* ([none (gensym)] [exn (with-handlers ([void (lambda (exn) exn)]) (if (module-path? mod) - (enter-require mod flags) + (dynamic-rerequire + mod + #:verbosity + (cond + [(memq '#:verbose flags) 'all] + [(memq '#:verbose-reload flags) 'reload] + [else 'none])) (raise-argument-error 'enter! "module-path?" mod)) none)]) ;; Try to switch to the module namespace, @@ -59,94 +67,3 @@ (namespace-require 'racket/enter))))) (unless (eq? none exn) (raise exn))) (current-namespace orig-namespace)))) - -(struct mod (name timestamp depends)) - -(define loaded (make-hash)) - -(define (enter-require mod flags) - ;; Collect dependencies while loading: - (parameterize ([current-load/use-compiled - (enter-load/use-compiled (current-load/use-compiled) - #f flags)]) - (dynamic-require mod #f)) - ;; Reload anything that's not up to date: - (check-latest mod flags)) - -(define (enter-load/use-compiled orig re? flags) - (define notify - (if (or (memq '#:verbose flags) (and re? (memq '#:verbose-reload flags))) - (lambda (path) - (eprintf " [~aloading ~a]\n" (if re? "re-" "") path)) - void)) - (lambda (path name) - (if (and name - (not (and (pair? name) - (not (car name))))) - ;; Module load: - (with-handlers ([(lambda (exn) - (and (pair? name) - (exn:get-module-code? exn))) - (lambda (exn) - ;; Load-handler protocol: quiet failure when a - ;; submodule is not found - (void))]) - (let* ([code (get-module-code - path "compiled" - (lambda (e) - (parameterize ([compile-enforce-module-constants #f]) - (compile e))) - (lambda (ext loader?) (load-extension ext) #f) - #:notify notify)] - [dir (or (current-load-relative-directory) (current-directory))] - [path (path->complete-path path dir)] - [path (normal-case-path (simplify-path path))]) - ;; Record module timestamp and dependencies: - (define-values (ts actual-path) (get-timestamp path)) - (let ([a-mod (mod name - ts - (if code - (append-map cdr (module-compiled-imports code)) - null))]) - (hash-set! loaded path a-mod)) - ;; Evaluate the module: - (parameterize ([current-module-declare-source actual-path]) - (eval code)))) - ;; Not a module, or a submodule that we shouldn't load from source: - (begin (notify path) (orig path name))))) - -(define (get-timestamp path) - (let ([ts (file-or-directory-modify-seconds path #f (lambda () #f))]) - (if ts - (values ts path) - (if (regexp-match? #rx#"[.]rkt$" (path->bytes path)) - (let* ([alt-path (path-replace-suffix path #".ss")] - [ts (file-or-directory-modify-seconds alt-path #f (lambda () #f))]) - (if ts - (values ts alt-path) - (values -inf.0 path))) - (values -inf.0 path))))) - -(define (check-latest mod flags) - (define mpi (module-path-index-join mod #f)) - (define done (make-hash)) - (let loop ([mpi mpi]) - (define rpath (module-path-index-resolve mpi)) - (define path (let ([p (resolved-module-path-name rpath)]) - (if (pair? p) (car p) p))) - (when (path? path) - (define npath (normal-case-path path)) - (unless (hash-ref done npath #f) - (hash-set! done npath #t) - (define mod (hash-ref loaded npath #f)) - (when mod - (for-each loop (mod-depends mod)) - (define-values (ts actual-path) (get-timestamp npath)) - (when (ts . > . (mod-timestamp mod)) - (define orig (current-load/use-compiled)) - (parameterize ([current-load/use-compiled - (enter-load/use-compiled orig #f flags)] - [current-module-declare-name rpath] - [current-module-declare-source actual-path]) - ((enter-load/use-compiled orig #t flags) - npath (mod-name mod))))))))) diff --git a/collects/racket/rerequire.rkt b/collects/racket/rerequire.rkt new file mode 100644 index 0000000000..5308f0e1f4 --- /dev/null +++ b/collects/racket/rerequire.rkt @@ -0,0 +1,104 @@ +#lang racket/base + +(require syntax/modcode) + +(provide dynamic-rerequire) + +(define (dynamic-rerequire mod #:verbosity [verbosity 'reload]) + (unless (module-path? mod) + (raise-argument-error 'dynamic-rerequire "module-path?" mod)) + (unless (memq verbosity '(all reload none)) + (raise-argument-error 'dynamic-rerequire "(or/c 'all 'reload 'none)" verbosity)) + (rerequire mod verbosity)) + +(struct mod (name timestamp depends)) + +(define loaded (make-hash)) + +(define (rerequire mod verbosity) + ;; Collect dependencies while loading: + (parameterize ([current-load/use-compiled + (rerequire-load/use-compiled (current-load/use-compiled) + #f verbosity)]) + (dynamic-require mod 0)) + ;; Reload anything that's not up to date: + (check-latest mod verbosity)) + +(define (rerequire-load/use-compiled orig re? verbosity) + (define notify + (if (or (eq? 'all verbosity) (and re? (eq? 'reload verbosity))) + (lambda (path) + (eprintf " [~aloading ~a]\n" (if re? "re-" "") path)) + void)) + (lambda (path name) + (if (and name + (not (and (pair? name) + (not (car name))))) + ;; Module load: + (with-handlers ([(lambda (exn) + (and (pair? name) + (exn:get-module-code? exn))) + (lambda (exn) + ;; Load-handler protocol: quiet failure when a + ;; submodule is not found + (void))]) + (let* ([code (get-module-code + path "compiled" + (lambda (e) + (parameterize ([compile-enforce-module-constants #f]) + (compile e))) + (lambda (ext loader?) (load-extension ext) #f) + #:notify notify)] + [dir (or (current-load-relative-directory) (current-directory))] + [path (path->complete-path path dir)] + [path (normal-case-path (simplify-path path))]) + ;; Record module timestamp and dependencies: + (define-values (ts actual-path) (get-timestamp path)) + (let ([a-mod (mod name + ts + (if code + (apply append + (map cdr (module-compiled-imports code))) + null))]) + (hash-set! loaded path a-mod)) + ;; Evaluate the module: + (parameterize ([current-module-declare-source actual-path]) + (eval code)))) + ;; Not a module, or a submodule that we shouldn't load from source: + (begin (notify path) (orig path name))))) + +(define (get-timestamp path) + (let ([ts (file-or-directory-modify-seconds path #f (lambda () #f))]) + (if ts + (values ts path) + (if (regexp-match? #rx#"[.]rkt$" (path->bytes path)) + (let* ([alt-path (path-replace-suffix path #".ss")] + [ts (file-or-directory-modify-seconds alt-path #f (lambda () #f))]) + (if ts + (values ts alt-path) + (values -inf.0 path))) + (values -inf.0 path))))) + +(define (check-latest mod verbosity) + (define mpi (module-path-index-join mod #f)) + (define done (make-hash)) + (let loop ([mpi mpi]) + (define rpath (module-path-index-resolve mpi)) + (define path (let ([p (resolved-module-path-name rpath)]) + (if (pair? p) (car p) p))) + (when (path? path) + (define npath (normal-case-path path)) + (unless (hash-ref done npath #f) + (hash-set! done npath #t) + (define mod (hash-ref loaded npath #f)) + (when mod + (for-each loop (mod-depends mod)) + (define-values (ts actual-path) (get-timestamp npath)) + (when (ts . > . (mod-timestamp mod)) + (define orig (current-load/use-compiled)) + (parameterize ([current-load/use-compiled + (rerequire-load/use-compiled orig #f verbosity)] + [current-module-declare-name rpath] + [current-module-declare-source actual-path]) + ((rerequire-load/use-compiled orig #t verbosity) + npath (mod-name mod))))))))) diff --git a/collects/scribblings/reference/enter.scrbl b/collects/scribblings/reference/enter.scrbl index c055070850..c3fe283c88 100644 --- a/collects/scribblings/reference/enter.scrbl +++ b/collects/scribblings/reference/enter.scrbl @@ -1,7 +1,9 @@ #lang scribble/doc -@(require "mz.rkt" (for-label racket/enter)) +@(require "mz.rkt" + (for-label racket/enter + racket/rerequire)) -@title[#:tag "enter"]{Interactive Module Loading} +@title[#:tag "enter"]{Entering Modules} @note-init-lib[racket/enter] @@ -16,29 +18,19 @@ Intended for use in a @tech{REPL}, such as when @exec{racket} is started in interactive mode. When a @racket[module-path] is provided (in the same sense as for @racket[require]), the corresponding module -is loaded or invoked, and the current @tech{namespace} is changed to +is loaded or invoked via @racket[dynamic-rerequire], and the current @tech{namespace} is changed to the body of the module via @racket[module->namespace]. When @racket[#f] is provided, then the current @tech{namespace} is restored to the original one. -If invoking @racket[module-path] requires loading any files, then -modification dates of the files are recorded. If the file is modified, -then a later @racket[enter!] re-loads the module from source; see also -@secref["module-redeclare"]. Similarly if a later @racket[enter!] -transitively @racket[require]s a modified module, then the required -module is re-loaded. Re-loading support works only for modules that -are first loaded (either directly or indirectly through transitive -@racket[require]s) via @racket[enter!]. - Additional @racket[flag]s can customize aspects of @racket[enter!]: @itemize[ - @item{When @racket[enter!] loads or re-loads a module from a file, it - can print a message to @racket[(current-error-port)]. Use the - @racket[#:verbose] flag to print a message about such loads and - re-loads, @racket[#:verbose-reload] to print a message only for - re-loaded modules, and @racket[#:quiet] for no printouts. The default - reporting corresponds to @racket[#:verbose-reload].} + @item{The @racket[#:verbose], @racket[#:verbose-reload], and + @racket[#:quiet] flags correspond to @racket['all], + @racket['reload], and @racket['none] verbosity for + @racket[dynamic-rerequire]. The default corresponds to + @racket[#:verbose-reload].} @item{After switching namespaces to the designated module, @racket[enter!] automatically requires @racket[racket/enter] into the diff --git a/collects/scribblings/reference/interactive.scrbl b/collects/scribblings/reference/interactive.scrbl new file mode 100644 index 0000000000..489a8205c7 --- /dev/null +++ b/collects/scribblings/reference/interactive.scrbl @@ -0,0 +1,11 @@ +#lang scribble/doc +@(require "mz.rkt") + +@title[#:tag "interactive"]{Interactive Module Loading} + +The @racketmodname[racket/rerequire] and @racketmodname[racket/enter] +libraries provide support for loading, reloading, and using modules. + +@include-section["enter.scrbl"] + +@include-section["rerequire.scrbl"] diff --git a/collects/scribblings/reference/rerequire.scrbl b/collects/scribblings/reference/rerequire.scrbl new file mode 100644 index 0000000000..d20e808515 --- /dev/null +++ b/collects/scribblings/reference/rerequire.scrbl @@ -0,0 +1,29 @@ +#lang scribble/doc +@(require "mz.rkt" (for-label racket/rerequire)) + +@title[#:tag "rerequire"]{Loading and Reloading Modules} + +@note-lib-only[racket/rerequire] + +@defproc[(dynamic-rerequire [module-path module-path?] + [#:verbosity verbosity (or/c 'all 'reload 'none) 'reload]) + void?]{ + +Like @racket[(dynamic-require module-path 0)], but with reloading +support. The @racket[dynamic-rerequire] function is intended for use +in an interactive environment, especially via @racket[enter!]. + +If invoking @racket[module-path] requires loading any files, then +modification dates of the files are recorded. If the file is modified, +then a later @racket[dynamic-rerequire] re-loads the module from source; see also +@secref["module-redeclare"]. Similarly if a later @racket[dynamic-rerequire] +transitively @racket[require]s a modified module, then the required +module is re-loaded. Re-loading support works only for modules that +are first loaded (either directly or indirectly through transitive +@racket[require]s) via @racket[dynamic-rerequire]. + +When @racket[enter!] loads or re-loads a module from a file, it can +print a message to @racket[(current-error-port)], depending on +@racket[verbosity]: @racket['verbose] prints a message for all loads and +re-loads, @racket['reload] prints a message only for +re-loaded modules, and @racket['none] disables printouts.} diff --git a/collects/scribblings/reference/running.scrbl b/collects/scribblings/reference/running.scrbl index d78dbd4905..aa8df1f75e 100644 --- a/collects/scribblings/reference/running.scrbl +++ b/collects/scribblings/reference/running.scrbl @@ -8,6 +8,6 @@ @include-section["startup.scrbl"] @include-section["collects.scrbl"] @include-section["help.scrbl"] -@include-section["enter.scrbl"] +@include-section["interactive.scrbl"] @include-section["debugging.scrbl"] @include-section["kernel.scrbl"] diff --git a/doc/release-notes/racket/HISTORY.txt b/doc/release-notes/racket/HISTORY.txt index 2fe1f7de5f..4c00342b7c 100644 --- a/doc/release-notes/racket/HISTORY.txt +++ b/doc/release-notes/racket/HISTORY.txt @@ -1,5 +1,6 @@ Version 5.3.4.8 Add `configure-runtime' submodule support +racket/rerequire: added Version 5.3.4.7 scribble: use `doc' submodule when available