add `racket/rerequire'

The `dynamic-rerequire' function is the reloading support of
`enter!', but in function form and without namespace-switching.
This commit is contained in:
Matthew Flatt 2013-05-07 10:48:44 -04:00
parent 4ac6a6b3e3
commit 376dd5f4aa
7 changed files with 166 additions and 112 deletions

View File

@ -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)))))))))

View File

@ -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)))))))))

View File

@ -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

View File

@ -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"]

View File

@ -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.}

View File

@ -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"]

View File

@ -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