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:
parent
4ac6a6b3e3
commit
376dd5f4aa
|
@ -1,6 +1,8 @@
|
||||||
#lang racket/base
|
#lang racket/base
|
||||||
|
|
||||||
(require syntax/modcode racket/list (for-syntax racket/base))
|
(require "rerequire.rkt"
|
||||||
|
racket/list
|
||||||
|
(for-syntax racket/base))
|
||||||
|
|
||||||
(provide enter!)
|
(provide enter!)
|
||||||
|
|
||||||
|
@ -38,7 +40,13 @@
|
||||||
(let* ([none (gensym)]
|
(let* ([none (gensym)]
|
||||||
[exn (with-handlers ([void (lambda (exn) exn)])
|
[exn (with-handlers ([void (lambda (exn) exn)])
|
||||||
(if (module-path? mod)
|
(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))
|
(raise-argument-error 'enter! "module-path?" mod))
|
||||||
none)])
|
none)])
|
||||||
;; Try to switch to the module namespace,
|
;; Try to switch to the module namespace,
|
||||||
|
@ -59,94 +67,3 @@
|
||||||
(namespace-require 'racket/enter)))))
|
(namespace-require 'racket/enter)))))
|
||||||
(unless (eq? none exn) (raise exn)))
|
(unless (eq? none exn) (raise exn)))
|
||||||
(current-namespace orig-namespace))))
|
(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)))))))))
|
|
||||||
|
|
104
collects/racket/rerequire.rkt
Normal file
104
collects/racket/rerequire.rkt
Normal 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)))))))))
|
|
@ -1,7 +1,9 @@
|
||||||
#lang scribble/doc
|
#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]
|
@note-init-lib[racket/enter]
|
||||||
|
|
||||||
|
@ -16,29 +18,19 @@
|
||||||
Intended for use in a @tech{REPL}, such as when @exec{racket} is
|
Intended for use in a @tech{REPL}, such as when @exec{racket} is
|
||||||
started in interactive mode. When a @racket[module-path] is provided
|
started in interactive mode. When a @racket[module-path] is provided
|
||||||
(in the same sense as for @racket[require]), the corresponding module
|
(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
|
the body of the module via @racket[module->namespace]. When
|
||||||
@racket[#f] is provided, then the current @tech{namespace} is restored
|
@racket[#f] is provided, then the current @tech{namespace} is restored
|
||||||
to the original one.
|
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!]:
|
Additional @racket[flag]s can customize aspects of @racket[enter!]:
|
||||||
@itemize[
|
@itemize[
|
||||||
|
|
||||||
@item{When @racket[enter!] loads or re-loads a module from a file, it
|
@item{The @racket[#:verbose], @racket[#:verbose-reload], and
|
||||||
can print a message to @racket[(current-error-port)]. Use the
|
@racket[#:quiet] flags correspond to @racket['all],
|
||||||
@racket[#:verbose] flag to print a message about such loads and
|
@racket['reload], and @racket['none] verbosity for
|
||||||
re-loads, @racket[#:verbose-reload] to print a message only for
|
@racket[dynamic-rerequire]. The default corresponds to
|
||||||
re-loaded modules, and @racket[#:quiet] for no printouts. The default
|
@racket[#:verbose-reload].}
|
||||||
reporting corresponds to @racket[#:verbose-reload].}
|
|
||||||
|
|
||||||
@item{After switching namespaces to the designated module,
|
@item{After switching namespaces to the designated module,
|
||||||
@racket[enter!] automatically requires @racket[racket/enter] into the
|
@racket[enter!] automatically requires @racket[racket/enter] into the
|
||||||
|
|
11
collects/scribblings/reference/interactive.scrbl
Normal file
11
collects/scribblings/reference/interactive.scrbl
Normal 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"]
|
29
collects/scribblings/reference/rerequire.scrbl
Normal file
29
collects/scribblings/reference/rerequire.scrbl
Normal 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.}
|
|
@ -8,6 +8,6 @@
|
||||||
@include-section["startup.scrbl"]
|
@include-section["startup.scrbl"]
|
||||||
@include-section["collects.scrbl"]
|
@include-section["collects.scrbl"]
|
||||||
@include-section["help.scrbl"]
|
@include-section["help.scrbl"]
|
||||||
@include-section["enter.scrbl"]
|
@include-section["interactive.scrbl"]
|
||||||
@include-section["debugging.scrbl"]
|
@include-section["debugging.scrbl"]
|
||||||
@include-section["kernel.scrbl"]
|
@include-section["kernel.scrbl"]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
Version 5.3.4.8
|
Version 5.3.4.8
|
||||||
Add `configure-runtime' submodule support
|
Add `configure-runtime' submodule support
|
||||||
|
racket/rerequire: added
|
||||||
|
|
||||||
Version 5.3.4.7
|
Version 5.3.4.7
|
||||||
scribble: use `doc' submodule when available
|
scribble: use `doc' submodule when available
|
||||||
|
|
Loading…
Reference in New Issue
Block a user