gexp: 'local-file' resolves relative file names.

* guix/gexp.scm (<local-file>): Rename constructor to '%%local-file'.
Add 'absolute' field.
(%local-file, extract-directory, absolute-file-name): New procedures.
(current-source-directory): New macro.
(local-file): Adjust call to '%local-file'.
(local-file-absolute-file-name): New procedure.
(local-file-compiler): Force the 'absolute' field.
* tests/guix-system.sh: Test whether 'local-file' canonicalization
works.
* doc/guix.texi (G-Expressions): Adjust.
This commit is contained in:
Ludovic Courtès 2015-12-14 19:52:47 +01:00
parent 4fef1e850e
commit 9d3994f700
3 changed files with 87 additions and 16 deletions

View File

@ -3489,8 +3489,9 @@ content is directly passed as a string.
@deffn {Scheme Procedure} local-file @var{file} [@var{name}] @ @deffn {Scheme Procedure} local-file @var{file} [@var{name}] @
[#:recursive? #t] [#:recursive? #t]
Return an object representing local file @var{file} to add to the store; this Return an object representing local file @var{file} to add to the store; this
object can be used in a gexp. @var{file} will be added to the store under @var{name}--by object can be used in a gexp. If @var{file} is a relative file name, it is looked
default the base name of @var{file}. up relative to the source file where this form appears. @var{file} will be added to
the store under @var{name}--by default the base name of @var{file}.
When @var{recursive?} is true, the contents of @var{file} are added recursively; if @var{file} When @var{recursive?} is true, the contents of @var{file} are added recursively; if @var{file}
designates a flat file and @var{recursive?} is true, its contents are added, and its designates a flat file and @var{recursive?} is true, its contents are added, and its

View File

@ -35,6 +35,7 @@
local-file local-file
local-file? local-file?
local-file-file local-file-file
local-file-absolute-file-name
local-file-name local-file-name
local-file-recursive? local-file-recursive?
@ -182,35 +183,76 @@ cross-compiling.)"
;;; File declarations. ;;; File declarations.
;;; ;;;
;; A local file name. FILE is the file name the user entered, which can be a
;; relative file name, and ABSOLUTE is a promise that computes its canonical
;; absolute file name. We keep it in a promise to compute it lazily and avoid
;; repeated 'stat' calls.
(define-record-type <local-file> (define-record-type <local-file>
(%local-file file name recursive?) (%%local-file file absolute name recursive?)
local-file? local-file?
(file local-file-file) ;string (file local-file-file) ;string
(absolute %local-file-absolute-file-name) ;promise string
(name local-file-name) ;string (name local-file-name) ;string
(recursive? local-file-recursive?)) ;Boolean (recursive? local-file-recursive?)) ;Boolean
(define* (local-file file #:optional (name (basename file)) (define* (%local-file file promise #:optional (name (basename file))
#:key recursive?) #:key recursive?)
;; This intermediate procedure is part of our ABI, but the underlying
;; %%LOCAL-FILE is not.
(%%local-file file promise name recursive?))
(define (extract-directory properties)
"Extract the directory name from source location PROPERTIES."
(match (assq 'filename properties)
(('filename . (? string? file-name))
(dirname file-name))
(_
#f)))
(define-syntax-rule (current-source-directory)
"Expand to the directory of the current source file or #f if it could not
be determined."
(extract-directory (current-source-location)))
(define (absolute-file-name file directory)
"Return the canonical absolute file name for FILE, which lives in the
vicinity of DIRECTORY."
(canonicalize-path
(cond ((string-prefix? "/" file) file)
((not directory) file)
((string-prefix? "/" directory)
(string-append directory "/" file))
(else file))))
(define-syntax-rule (local-file file rest ...)
"Return an object representing local file FILE to add to the store; this "Return an object representing local file FILE to add to the store; this
object can be used in a gexp. FILE will be added to the store under NAME--by object can be used in a gexp. If FILE is a relative file name, it is looked
default the base name of FILE. up relative to the source file where this form appears. FILE will be added to
the store under NAME--by default the base name of FILE.
When RECURSIVE? is true, the contents of FILE are added recursively; if FILE When RECURSIVE? is true, the contents of FILE are added recursively; if FILE
designates a flat file and RECURSIVE? is true, its contents are added, and its designates a flat file and RECURSIVE? is true, its contents are added, and its
permission bits are kept. permission bits are kept.
This is the declarative counterpart of the 'interned-file' monadic procedure." This is the declarative counterpart of the 'interned-file' monadic procedure."
;; Canonicalize FILE so that if it's a symlink, it is resolved. Failing to (%local-file file
;; do that, when RECURSIVE? is #t, we could end up creating a dangling (delay (absolute-file-name file (current-source-directory)))
;; symlink in the store, and when RECURSIVE? is #f 'add-to-store' would just rest ...))
;; throw an error, both of which are inconvenient.
(%local-file (canonicalize-path file) name recursive?)) (define (local-file-absolute-file-name file)
"Return the absolute file name for FILE, a <local-file> instance. A
'system-error' exception is raised if FILE could not be found."
(force (%local-file-absolute-file-name file)))
(define-gexp-compiler (local-file-compiler (file local-file?) system target) (define-gexp-compiler (local-file-compiler (file local-file?) system target)
;; "Compile" FILE by adding it to the store. ;; "Compile" FILE by adding it to the store.
(match file (match file
(($ <local-file> file name recursive?) (($ <local-file> file (= force absolute) name recursive?)
(interned-file file name #:recursive? recursive?)))) ;; Canonicalize FILE so that if it's a symlink, it is resolved. Failing
;; to do that, when RECURSIVE? is #t, we could end up creating a dangling
;; symlink in the store, and when RECURSIVE? is #f 'add-to-store' would
;; just throw an error, both of which are inconvenient.
(interned-file absolute name #:recursive? recursive?))))
(define-record-type <plain-file> (define-record-type <plain-file>
(%plain-file name content references) (%plain-file name content references)

View File

@ -17,7 +17,7 @@
# along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. # along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
# #
# Test the daemon and its interaction with 'guix substitute'. # Test 'guix system', mostly error reporting.
# #
set -e set -e
@ -26,7 +26,15 @@ guix system --version
tmpfile="t-guix-system-$$" tmpfile="t-guix-system-$$"
errorfile="t-guix-system-error-$$" errorfile="t-guix-system-error-$$"
trap 'rm -f "$tmpfile" "$errorfile"' EXIT
# Note: This directory is chosen outside $builddir so that relative file name
# canonicalization doesn't mess up with 'current-source-directory', used by
# 'local-file' ('load' forces 'relative' for
# %FILE-PORT-NAME-CANONICALIZATION.)
tmpdir="${TMPDIR:-/tmp}/t-guix-system-$$"
mkdir "$tmpdir"
trap 'rm -f "$tmpfile" "$errorfile" "$tmpdir"/*; rmdir "$tmpdir"' EXIT
# Reporting of syntax errors. # Reporting of syntax errors.
@ -180,3 +188,23 @@ make_user_config "users" "group-that-does-not-exist"
if guix system build "$tmpfile" -n 2> "$errorfile" if guix system build "$tmpfile" -n 2> "$errorfile"
then false then false
else grep "supplementary group.*group-that-does-not-exist.*undeclared" "$errorfile"; fi else grep "supplementary group.*group-that-does-not-exist.*undeclared" "$errorfile"; fi
# Try 'local-file' and relative file name resolution.
cat > "$tmpdir/config.scm"<<EOF
(use-modules (gnu))
(use-service-modules networking)
(operating-system
$OS_BASE
(services (cons (tor-service (local-file "my-torrc"))
%base-services)))
EOF
cat > "$tmpdir/my-torrc"<<EOF
# This is an example file.
EOF
# In both cases 'my-torrc' should be properly resolved.
guix system build "$tmpdir/config.scm" -n
(cd "$tmpdir"; guix system build "config.scm" -n)