union: Add 'relative-file-name'.

* guix/build/union.scm (%not-slash): New variable.
(relative-file-name): New procedure.
* tests/union.scm (test-relative-file-name): New macro and tests.
This commit is contained in:
Ludovic Courtès 2018-04-28 17:17:33 +02:00
parent 8584965b79
commit dac1c97d13
No known key found for this signature in database
GPG Key ID: 090B11993D9AEBB5
3 changed files with 59 additions and 2 deletions

View File

@ -27,7 +27,9 @@
#:use-module (rnrs io ports) #:use-module (rnrs io ports)
#:export (union-build #:export (union-build
warn-about-collision)) warn-about-collision
relative-file-name))
;;; Commentary: ;;; Commentary:
;;; ;;;
@ -174,4 +176,41 @@ returns #f, skip the faulty file altogether."
(union-of-directories output (delete-duplicates inputs))) (union-of-directories output (delete-duplicates inputs)))
;;;
;;; Relative symlinks.
;;;
(define %not-slash
(char-set-complement (char-set #\/)))
(define (relative-file-name reference file)
"Given REFERENCE and FILE, both of which are absolute file names, return the
file name of FILE relative to REFERENCE.
(relative-file-name \"/gnu/store/foo\" \"/gnu/store/bin/bar\")
=> \"../bin/bar\"
Note that this is from a purely lexical standpoint; conversely, \"..\" is
*not* resolved lexically on POSIX in the presence of symlinks."
(if (and (string-prefix? "/" file) (string-prefix? "/" reference))
(let loop ((reference (string-tokenize reference %not-slash))
(file (string-tokenize file %not-slash)))
(define (finish)
(string-join (append (make-list (length reference) "..") file)
"/"))
(match reference
(()
(finish))
((head . tail)
(match file
(()
(finish))
((head* . tail*)
(if (string=? head head*)
(loop tail tail*)
(finish)))))))
file))
;;; union.scm ends here ;;; union.scm ends here

View File

@ -184,4 +184,22 @@
(file-is-directory? "bin") (file-is-directory? "bin")
(eq? 'symlink (stat:type (lstat "bin/guile")))))))) (eq? 'symlink (stat:type (lstat "bin/guile"))))))))
(letrec-syntax ((test-relative-file-name
(syntax-rules (=>)
((_ (reference file => expected) rest ...)
(begin
(test-equal (string-append "relative-file-name "
reference " " file)
expected
(relative-file-name reference file))
(test-relative-file-name rest ...)))
((_)
#t))))
(test-relative-file-name
("/a/b" "/a/c/d" => "../c/d")
("/a/b" "/a/b" => "")
("/a/b" "/a" => "..")
("/a/b" "/a/b/c/d" => "c/d")
("/a/b/c" "/a/d/e/f" => "../../d/e/f")))
(test-end) (test-end)

View File

@ -1,5 +1,5 @@
;;; GNU Guix --- Functional package management for GNU ;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017 Ludovic Courtès <ludo@gnu.org> ;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2014 Eric Bavier <bavier@member.fsf.org> ;;; Copyright © 2014 Eric Bavier <bavier@member.fsf.org>
;;; Copyright © 2016 Mathieu Lirzin <mthl@gnu.org> ;;; Copyright © 2016 Mathieu Lirzin <mthl@gnu.org>
;;; ;;;