racket/collects/setup/path-relativize.rkt
2010-04-27 16:50:15 -06:00

81 lines
3.3 KiB
Racket

#lang racket/base
(require racket/promise)
(provide make-relativize)
(define (make-relativize find-main-dir tag to-rel-name from-rel-name)
;; Historical note: this module is based on the old "plthome.ss"
;; The `path->main-relative' and `main-relative->path' functions
;; that this generates are used to store paths that are relative to
;; the main directory (specified by `find-main-dir'), such as in
;; .dep files. This means that if the plt tree is moved, .dep files
;; still work. It is generally fine if `path->main-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 plt
;; tree, so we explode the paths. This slower than the old way (by
;; a factor of 2 or so), but it's simpler and more portable.
(define (explode-path* path)
(explode-path (simplify-path (path->complete-path path))))
(define (explode-path orig-path)
(let loop ([path orig-path][rest null])
(let-values ([(base name dir?) (split-path path)])
(if (path? base)
(loop base (cons name rest))
(cons name rest)))))
(define main-dir/
(delay (let ([dir (find-main-dir)])
(and dir (explode-path* dir)))))
;; path->main-relative* : path-or-bytes -> datum-containing-bytes-or-path
(define (path->main-relative* path)
(let loop ([exploded
(explode-path*
(cond [(bytes? path) (bytes->path path)]
[(path-string? path) path]
[else (raise-type-error
to-rel-name "path, string, or bytes" path)]))]
[main-exploded (force main-dir/)])
(cond [(null? main-exploded)
(cons tag (map path-element->bytes exploded))]
[(null? exploded) path]
[(equal? (normal-case-path (car exploded))
(normal-case-path (car main-exploded)))
(loop (cdr exploded) (cdr main-exploded))]
[else path])))
;; main-relative->path* : datum-containing-bytes-or-path -> path
(define (main-relative->path* path)
(cond [(and (pair? path)
(eq? tag (car path))
(or (bytes? (cdr path)) ; backward compatibility
(and (list? (cdr path)) (andmap bytes? (cdr path)))))
(let ([dir (or (find-main-dir)
;; No main "collects"/"doc"/whatever? Use
;; original working directory:
(find-system-path 'orig-dir))])
(if (bytes? (cdr path))
;; backward compatibilty:
(if (equal? (cdr path) #"")
dir
(build-path dir (bytes->path (cdr path))))
;; Normal mode:
(apply build-path dir
(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->main-relative*
main-relative->path*))