guix hash: Add '--recursive'.

* guix/scripts/hash.scm (show-help): Add --recursive.
  (%options): Likewise.
  (guix-hash)[file-hash]: New procedure.  Honor --recursive.
  Use it.
* guix/nar.scm (write-file): Add missing field to the &nar-error
  condition raised upon unsupported file type; change its message to be
  more descriptive.
* tests/guix-hash.sh: Add tests with -r.
* doc/guix.texi (Invoking guix hash): Document --recursive.
This commit is contained in:
Ludovic Courtès 2014-02-21 17:54:32 +01:00
parent c1d52c71aa
commit 3140f2df42
4 changed files with 57 additions and 7 deletions

View file

@ -1958,6 +1958,19 @@ If the @option{--format} option is not specified, @command{guix hash}
will output the hash in @code{nix-base32}. This representation is used will output the hash in @code{nix-base32}. This representation is used
in the definitions of packages. in the definitions of packages.
@item --recursive
@itemx -r
Compute the hash on @var{file} recursively.
In this case, the hash is computed on an archive containing @var{file},
including its children if it is a directory. Some of @var{file}'s
meta-data is part of the archive; for instance, when @var{file} is a
regular file, the hash is different depending on whether @var{file} is
executable or not. Meta-data such as time stamps has no impact on the
hash (@pxref{Invoking guix archive}).
@c FIXME: Replace xref above with xref to an ``Archive'' section when
@c it exists.
@end table @end table
@node Invoking guix refresh @node Invoking guix refresh

View file

@ -195,8 +195,8 @@ (define p port)
(write-string "target" p) (write-string "target" p)
(write-string (readlink f) p)) (write-string (readlink f) p))
(else (else
(raise (condition (&message (message "ENOSYS")) (raise (condition (&message (message "unsupported file type"))
(&nar-error))))) (&nar-error (file f) (port port))))))
(write-string ")" p)))) (write-string ")" p))))
(define (restore-file port file) (define (restore-file port file)

View file

@ -20,12 +20,14 @@
(define-module (guix scripts hash) (define-module (guix scripts hash)
#:use-module (guix base32) #:use-module (guix base32)
#:use-module (guix hash) #:use-module (guix hash)
#:use-module (guix nar)
#:use-module (guix ui) #:use-module (guix ui)
#:use-module (guix utils) #:use-module (guix utils)
#:use-module (rnrs io ports) #:use-module (rnrs io ports)
#:use-module (rnrs files) #:use-module (rnrs files)
#:use-module (ice-9 match) #:use-module (ice-9 match)
#:use-module (srfi srfi-1) #:use-module (srfi srfi-1)
#:use-module (srfi srfi-11)
#:use-module (srfi srfi-26) #:use-module (srfi srfi-26)
#:use-module (srfi srfi-37) #:use-module (srfi srfi-37)
#:export (guix-hash)) #:export (guix-hash))
@ -43,10 +45,12 @@ (define (show-help)
(display (_ "Usage: guix hash [OPTION] FILE (display (_ "Usage: guix hash [OPTION] FILE
Return the cryptographic hash of FILE. Return the cryptographic hash of FILE.
Supported formats: 'nix-base32' (default), 'base32', and 'base16' Supported formats: 'nix-base32' (default), 'base32', and 'base16' ('hex'
('hex' and 'hexadecimal' can be used as well).\n")) and 'hexadecimal' can be used as well).\n"))
(format #t (_ " (format #t (_ "
-f, --format=FMT write the hash in the given format")) -f, --format=FMT write the hash in the given format"))
(format #t (_ "
-r, --recursive compute the hash on FILE recursively"))
(newline) (newline)
(display (_ " (display (_ "
-h, --help display this help and exit")) -h, --help display this help and exit"))
@ -73,6 +77,9 @@ (define fmt-proc
(alist-cons 'format fmt-proc (alist-cons 'format fmt-proc
(alist-delete 'format result)))) (alist-delete 'format result))))
(option '(#\r "recursive") #f #f
(lambda (opt name arg result)
(alist-cons 'recursive? #t result)))
(option '(#\h "help") #f #f (option '(#\h "help") #f #f
(lambda args (lambda args
@ -107,12 +114,22 @@ (define (parse-options)
(reverse opts))) (reverse opts)))
(fmt (assq-ref opts 'format))) (fmt (assq-ref opts 'format)))
(define (file-hash file)
;; Compute the hash of FILE.
;; Catch and gracefully report possible '&nar-error' conditions.
(with-error-handling
(if (assoc-ref opts 'recursive?)
(let-values (((port get-hash) (open-sha256-port)))
(write-file file port)
(flush-output-port port)
(get-hash))
(call-with-input-file file port-sha256))))
(match args (match args
((file) ((file)
(catch 'system-error (catch 'system-error
(lambda () (lambda ()
(format #t "~a~%" (format #t "~a~%" (fmt (file-hash file))))
(fmt (call-with-input-file file port-sha256))))
(lambda args (lambda args
(leave (_ "~a~%") (leave (_ "~a~%")
(strerror (system-error-errno args)))))) (strerror (system-error-errno args))))))

View file

@ -1,5 +1,5 @@
# GNU Guix --- Functional package management for GNU # GNU Guix --- Functional package management for GNU
# Copyright © 2013 Ludovic Courtès <ludo@gnu.org> # Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
# #
# This file is part of GNU Guix. # This file is part of GNU Guix.
# #
@ -22,7 +22,27 @@
guix hash --version guix hash --version
tmpdir="guix-hash-$$"
trap 'rm -rf "$tmpdir"' EXIT
test `guix hash /dev/null` = 0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73 test `guix hash /dev/null` = 0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73
test `guix hash -f nix-base32 /dev/null` = 0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73 test `guix hash -f nix-base32 /dev/null` = 0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73
test `guix hash -f hex /dev/null` = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 test `guix hash -f hex /dev/null` = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
test `guix hash -f base32 /dev/null` = 4oymiquy7qobjgx36tejs35zeqt24qpemsnzgtfeswmrw6csxbkq test `guix hash -f base32 /dev/null` = 4oymiquy7qobjgx36tejs35zeqt24qpemsnzgtfeswmrw6csxbkq
mkdir "$tmpdir"
echo -n executable > "$tmpdir/exe"
chmod +x "$tmpdir/exe"
( cd "$tmpdir" ; ln -s exe symlink )
mkdir "$tmpdir/subdir"
test `guix hash -r "$tmpdir"` = 10k1lw41wyrjf9mxydi0is5nkpynlsvgslinics4ppir13g7d74p
# Without '-r', this should fail.
if guix hash "$tmpdir"
then false; else true; fi
# This should fail because /dev/null is a character device, which
# the archive format doesn't support.
if guix hash -r /dev/null
then false; else true; fi