racket/collects/setup/path-relativize.rkt
Matthew Flatt ee775c3cc3 intern strings, etc. only when making syntax objects, not in `read'
Rename `read-intern-literal' to `datum-intern-literal'.

Interning is needed only in `read-syntax' or `datum->syntax' to
set up the invariants that the bytecode compiler needs for cross-module
optimization. When `read'ing numbers from a data file, meanwhile,
interning slows things down a lot and doesn't seem worthwhile.
2011-12-14 16:03:44 -07:00

72 lines
3.1 KiB
Racket

#lang racket/base
(require racket/promise)
(provide make-relativize)
(define (make-relativize find-root-dir tag to-rel-name from-rel-name)
;; Historical note: this module is based on the old "plthome.ss"
;; The `path->relative' and `relative->path' functions that this
;; generates are used to store paths that are relative to the root
;; directory (specified by `find-root-dir'), such as in .dep files.
;; This means that if the racket tree is moved, .dep files still
;; work. It is generally fine if `path->relative' misses some
;; usages, as long as it works when we prepare a distribution tree.
;; (If it misses, things will continue to work fine and .dep files
;; will contain absolute path names.)
;; We need to compare paths to find when something is in the racket
;; tree, so we explode the paths. This is slower than the old way
;; (by a factor of 2 or so), but it's simpler and more portable.
(define (explode-path path)
(let loop ([path (simplify-path (path->complete-path path))]
[rest null])
(let-values ([(base name dir?) (split-path path)])
(if (path? base)
(loop base (cons name rest))
(cons name rest)))))
(define exploded-root
(delay (cond [(find-root-dir) => explode-path] [else #f])))
;; path->relative : path-or-bytes -> datum-containing-bytes-or-path
(define (path->relative path0)
(define path1
(cond [(bytes? path0) (bytes->path path0)]
[(path-string? path0) path0]
[else (raise-type-error to-rel-name "path, string, or bytes"
path0)]))
(let loop ([path (explode-path path1)] [root (force exploded-root)])
(cond [(null? root) (cons tag (map (lambda (pe) (datum-intern-literal (path-element->bytes pe)))
path))]
;; Note: in some cases this returns the input path as is, which
;; could be a byte string -- it should be possible to return
;; `path1', but that messes up the xform compilation somehow, by
;; having #<path...> vaules written into dep files.
[(null? path) path0]
[(equal? (car path) (car root)) (loop (cdr path) (cdr root))]
[else path0])))
(define root-or-orig
(delay (or (find-root-dir)
;; No main "collects"/"doc"/whatever => use the
;; original working directory:
(find-system-path 'orig-dir))))
;; relative->path : datum-containing-bytes-or-path -> path
(define (relative->path path)
(cond [(and (pair? path) (eq? tag (car path))
(and (list? (cdr path)) (andmap bytes? (cdr path))))
(apply build-path (force root-or-orig)
(map bytes->path-element (cdr path)))]
[(path? path) path]
[(bytes? path) (bytes->path path)]
[(string? path) (string->path path)]
[else (raise-type-error
from-rel-name
(format "path, string, bytes, or a list beginning with ~a" tag)
path)]))
(values path->relative relative->path))