diff options
author | Mark H Weaver <mhw@netris.org> | 2015-01-11 09:38:49 -0500 |
---|---|---|
committer | Mark H Weaver <mhw@netris.org> | 2015-01-11 09:38:49 -0500 |
commit | 77448857311318fc9cd866afcb85ca98fccdb25b (patch) | |
tree | efed3a71d1f7b2c2cc292e7e4ba1884c4d26a9e4 /guix | |
parent | 62c155c0bcbc0d71b1bc35e966193b6e8de03246 (diff) | |
parent | 0009ed71ad288358cbc7828954b5e1a3f18fd525 (diff) |
Merge branch 'master' into core-updates
Diffstat (limited to 'guix')
-rw-r--r-- | guix/base64.scm | 22 | ||||
-rw-r--r-- | guix/derivations.scm | 77 | ||||
-rw-r--r-- | guix/hash.scm | 5 | ||||
-rw-r--r-- | guix/import/cpan.scm | 167 | ||||
-rw-r--r-- | guix/import/gnu.scm | 7 | ||||
-rw-r--r-- | guix/import/json.scm | 32 | ||||
-rw-r--r-- | guix/import/pypi.scm | 47 | ||||
-rw-r--r-- | guix/import/utils.scm | 48 | ||||
-rw-r--r-- | guix/records.scm | 59 | ||||
-rw-r--r-- | guix/scripts/import.scm | 2 | ||||
-rw-r--r-- | guix/scripts/import/cpan.scm | 91 | ||||
-rwxr-xr-x | guix/scripts/substitute-binary.scm | 13 | ||||
-rw-r--r-- | guix/tests.scm | 11 | ||||
-rw-r--r-- | guix/ui.scm | 16 |
14 files changed, 471 insertions, 126 deletions
diff --git a/guix/base64.scm b/guix/base64.scm index f7f7f5f4e1..e4d2ec589b 100644 --- a/guix/base64.scm +++ b/guix/base64.scm @@ -4,6 +4,8 @@ ;; (guix base64) by Nikita Karetnikov <nikita@karetnikov.org> on ;; February 12, 2014. ;; +;; Some optimizations made by Ludovic Courtès <ludo@gnu.org>, 2015. +;; ;; Copyright © 2009, 2010 Göran Weinholt <goran@weinholt.se> ;; ;; This program is free software: you can redistribute it and/or modify @@ -33,7 +35,23 @@ (only (srfi :13 strings) string-index string-prefix? string-suffix? - string-concatenate string-trim-both)) + string-concatenate string-trim-both) + (only (guile) ash logior)) + + + (define-syntax define-alias + (syntax-rules () + ((_ new old) + (define-syntax new (identifier-syntax old))))) + + ;; Force the use of Guile's own primitives to avoid the overhead of its 'fx' + ;; procedures. + (define-alias fxbit-field bitwise-bit-field) + (define-alias fxarithmetic-shift ash) + (define-alias fxarithmetic-shift-left ash) + (define-alias fxand logand) + (define-alias fxior logior) + (define-alias fxxor logxor) (define base64-alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") @@ -209,4 +227,4 @@ line-length #f base64-alphabet port) (display (string-append "\n-----END " type "-----\n") port)) ((port type bv) - (put-delimited-base64 port type bv 76)))))
\ No newline at end of file + (put-delimited-base64 port type bv 76))))) diff --git a/guix/derivations.scm b/guix/derivations.scm index 69cef1a4cd..ec438e833c 100644 --- a/guix/derivations.scm +++ b/guix/derivations.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org> +;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org> ;;; ;;; This file is part of GNU Guix. ;;; @@ -58,9 +58,11 @@ derivation-input-output-paths derivation-name + derivation-output-names fixed-output-derivation? offloadable-derivation? substitutable-derivation? + substitution-oracle derivation-hash read-derivation @@ -135,6 +137,12 @@ (let ((base (store-path-package-name (derivation-file-name drv)))) (string-drop-right base 4))) +(define (derivation-output-names drv) + "Return the names of the outputs of DRV." + (match (derivation-outputs drv) + (((names . _) ...) + names))) + (define (fixed-output-derivation? drv) "Return #t if DRV is a fixed-output derivation, such as the result of a download with a fixed hash (aka. `fetchurl')." @@ -177,41 +185,52 @@ download with a fixed hash (aka. `fetchurl')." ;; synonymous, see <http://bugs.gnu.org/18747>. offloadable-derivation?) +(define (derivation-output-paths drv sub-drvs) + "Return the output paths of outputs SUB-DRVS of DRV." + (match drv + (($ <derivation> outputs) + (map (lambda (sub-drv) + (derivation-output-path (assoc-ref outputs sub-drv))) + sub-drvs)))) + +(define* (substitution-oracle store drv) + "Return a one-argument procedure that, when passed a store file name, +returns #t if it's substitutable and #f otherwise. The returned procedure +knows about all substitutes for all the derivations listed in DRV and their +prerequisites. + +Creating a single oracle (thus making a single 'substitutable-paths' call) and +reusing it is much more efficient than calling 'has-substitutes?' or similar +repeatedly, because it avoids the costs associated with launching the +substituter many times." + (let* ((paths (delete-duplicates + (fold (lambda (drv result) + (let ((self (match (derivation->output-paths drv) + (((names . paths) ...) + paths))) + (deps (append-map derivation-input-output-paths + (derivation-prerequisites + drv)))) + (append self deps result))) + '() + drv))) + (subst (substitutable-paths store paths))) + (cut member <> subst))) + (define* (derivation-prerequisites-to-build store drv #:key (outputs - (map - car - (derivation-outputs drv))) - (use-substitutes? #t)) + (derivation-output-names drv)) + (substitutable? + (substitution-oracle store + (list drv)))) "Return two values: the list of derivation-inputs required to build the OUTPUTS of DRV and not already available in STORE, recursively, and the list -of required store paths that can be substituted. When USE-SUBSTITUTES? is #f, -that second value is the empty list." - (define (derivation-output-paths drv sub-drvs) - (match drv - (($ <derivation> outputs) - (map (lambda (sub-drv) - (derivation-output-path (assoc-ref outputs sub-drv))) - sub-drvs)))) - +of required store paths that can be substituted. SUBSTITUTABLE? must be a +one-argument procedure similar to that returned by 'substitution-oracle'." (define built? (cut valid-path? store <>)) - (define substitutable? - ;; Return true if the given path is substitutable. Call - ;; `substitutable-paths' upfront, to benefit from parallelism in the - ;; substituter. - (if use-substitutes? - (let ((s (substitutable-paths store - (append - (derivation-output-paths drv outputs) - (append-map - derivation-input-output-paths - (derivation-prerequisites drv)))))) - (cut member <> s)) - (const #f))) - (define input-built? (compose (cut any built? <>) derivation-input-output-paths)) @@ -844,7 +863,7 @@ recursively." replacements)))) (derivation-builder-environment-vars drv)) #:inputs (append (map list sources) inputs) - #:outputs (map car (derivation-outputs drv)) + #:outputs (derivation-output-names drv) #:hash (match (derivation-outputs drv) ((($ <derivation-output> _ algo hash)) hash) diff --git a/guix/hash.scm b/guix/hash.scm index fb85f47586..593c2e1aee 100644 --- a/guix/hash.scm +++ b/guix/hash.scm @@ -26,6 +26,7 @@ #:export (sha256 open-sha256-port port-sha256 + file-sha256 open-sha256-input-port)) ;;; Commentary: @@ -129,6 +130,10 @@ output port." (close-port out) (get))) +(define (file-sha256 file) + "Return the SHA256 hash (a bytevector) of FILE." + (call-with-input-file file port-sha256)) + (define (open-sha256-input-port port) "Return an input port that wraps PORT and a thunk to get the hash of all the data read from PORT. The thunk always returns the same value." diff --git a/guix/import/cpan.scm b/guix/import/cpan.scm new file mode 100644 index 0000000000..5f4602a8d2 --- /dev/null +++ b/guix/import/cpan.scm @@ -0,0 +1,167 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2014 Eric Bavier <bavier@member.fsf.org> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + +(define-module (guix import cpan) + #:use-module (ice-9 match) + #:use-module (ice-9 regex) + #:use-module (srfi srfi-1) + #:use-module (json) + #:use-module (guix hash) + #:use-module (guix store) + #:use-module (guix base32) + #:use-module ((guix download) #:select (download-to-store)) + #:use-module (guix import utils) + #:use-module (guix import json) + #:export (cpan->guix-package)) + +;;; Commentary: +;;; +;;; Generate a package declaration template for the latest version of a CPAN +;;; module, using meta-data from metacpan.org. +;;; +;;; Code: + +(define string->license + (match-lambda + ;; List of valid values from https://metacpan.org/pod/CPAN::Meta::Spec. + ;; Some licenses are excluded based on their absense from (guix licenses). + ("agpl_3" 'agpl3) + ;; apache_1_1 + ("apache_2_0" 'asl2.0) + ;; artistic_1_0 + ;; artistic_2_0 + ("bsd" 'bsd-3) + ("freebsd" 'bsd-2) + ;; gfdl_1_2 + ("gfdl_1_3" 'fdl1.3+) + ("gpl_1" 'gpl1) + ("gpl_2" 'gpl2) + ("gpl_3" 'gpl3) + ("lgpl_2_1" 'lgpl2.1) + ("lgpl_3_0" 'lgpl3) + ("mit" 'x11) + ;; mozilla_1_0 + ("mozilla_1_1" 'mpl1.1) + ("openssl" 'openssl) + ("perl_5" 'gpl1+) ;and Artistic 1 + ("qpl_1_0" 'qpl) + ;; ssleay + ;; sun + ("zlib" 'zlib) + ((x) (string->license x)) + ((lst ...) `(list ,@(map string->license lst))) + (_ #f))) + +(define (module->name module) + "Transform a 'module' name into a 'release' name" + (regexp-substitute/global #f "::" module 'pre "-" 'post)) + +(define (cpan-fetch module) + "Return an alist representation of the CPAN metadata for the perl module MODULE, +or #f on failure. MODULE should be e.g. \"Test::Script\"" + ;; This API always returns the latest release of the module. + (json-fetch (string-append "http://api.metacpan.org/release/" + ;; XXX: The 'release' api requires the "release" + ;; name of the package. This substitution seems + ;; reasonably consistent across packages. + (module->name module)))) + +(define (cpan-home name) + (string-append "http://search.cpan.org/dist/" name)) + +(define (cpan-module->sexp meta) + "Return the `package' s-expression for a CPAN module from the metadata in +META." + (define name + (assoc-ref meta "distribution")) + + (define (guix-name name) + (if (string-prefix? "perl-" name) + (string-downcase name) + (string-append "perl-" (string-downcase name)))) + + (define version + (assoc-ref meta "version")) + + (define (convert-inputs phases) + ;; Convert phase dependencies into a list of name/variable pairs. + (match (flatten + (map (lambda (ph) + (filter-map (lambda (t) + (assoc-ref* meta "metadata" "prereqs" ph t)) + '("requires" "recommends" "suggests"))) + phases)) + (#f + '()) + ((inputs ...) + (delete-duplicates + ;; Listed dependencies may include core modules. Filter those out. + (filter-map (match-lambda + ((or (module . "0") ("perl" . _)) + ;; TODO: A stronger test might to run MODULE through + ;; `corelist' from our perl package. This current test + ;; seems to be only a loose convention. + #f) + ((module . _) + (let ((name (guix-name (module->name module)))) + (list name + (list 'unquote (string->symbol name)))))) + inputs))))) + + (define (maybe-inputs guix-name inputs) + (match inputs + (() + '()) + ((inputs ...) + (list (list guix-name + (list 'quasiquote inputs)))))) + + (define source-url + (assoc-ref meta "download_url")) + + (let ((tarball (with-store store + (download-to-store store source-url)))) + `(package + (name ,(guix-name name)) + (version ,version) + (source (origin + (method url-fetch) + (uri (string-append ,@(factorize-uri source-url version))) + (sha256 + (base32 + ,(bytevector->nix-base32-string (file-sha256 tarball)))))) + (build-system perl-build-system) + ,@(maybe-inputs 'native-inputs + ;; "runtime" and "test" may also be needed here. See + ;; https://metacpan.org/pod/CPAN::Meta::Spec#Phases, + ;; which says they are required during building. We + ;; have not yet had a need for cross-compiled perl + ;; modules, however, so we leave them out. + (convert-inputs '("configure" "build"))) + ,@(maybe-inputs 'inputs + (convert-inputs '("runtime"))) + (home-page ,(string-append "http://search.cpan.org/dist/" name)) + (synopsis ,(assoc-ref meta "abstract")) + (description fill-in-yourself!) + (license ,(string->license (assoc-ref meta "license")))))) + +(define (cpan->guix-package module-name) + "Fetch the metadata for PACKAGE-NAME from metacpan.org, and return the +`package' s-expression corresponding to that package, or #f on failure." + (let ((module-meta (cpan-fetch module-name))) + (and=> module-meta cpan-module->sexp))) diff --git a/guix/import/gnu.scm b/guix/import/gnu.scm index 1947f489fb..7160fcf7ba 100644 --- a/guix/import/gnu.scm +++ b/guix/import/gnu.scm @@ -18,6 +18,7 @@ (define-module (guix import gnu) #:use-module (guix gnu-maintenance) + #:use-module (guix import utils) #:use-module (guix utils) #:use-module (guix store) #:use-module (guix hash) @@ -38,10 +39,6 @@ ;;; ;;; Code: -(define (file-sha256 file) - "Return the SHA256 hash of FILE as a bytevector." - (call-with-input-file file port-sha256)) - (define (qualified-url url) "Return a fully-qualified URL based on URL." (if (string-prefix? "/" url) @@ -102,7 +99,7 @@ details.)" (let ((version (gnu-release-version release))) (match (find-packages (regexp-quote name)) ((info . _) - (gnu-package->sexp info release)) + (gnu-package->sexp info release #:key-download key-download)) (() (raise (condition (&message diff --git a/guix/import/json.scm b/guix/import/json.scm new file mode 100644 index 0000000000..c3092a5a9d --- /dev/null +++ b/guix/import/json.scm @@ -0,0 +1,32 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2014 David Thompson <davet@gnu.org> +;;; Copyright © 2015 Eric Bavier <bavier@member.fsf.org> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + +(define-module (guix import json) + #:use-module (json) + #:use-module (guix utils) + #:use-module (guix import utils) + #:export (json-fetch)) + +(define (json-fetch url) + "Return an alist representation of the JSON resource URL, or #f on failure." + (call-with-temporary-output-file + (lambda (temp port) + (and (url-fetch url temp) + (hash-table->alist + (call-with-input-file temp json->scm)))))) diff --git a/guix/import/pypi.scm b/guix/import/pypi.scm index 88f4a8e896..8567cad79c 100644 --- a/guix/import/pypi.scm +++ b/guix/import/pypi.scm @@ -27,40 +27,15 @@ #:use-module (web uri) #:use-module (guix utils) #:use-module (guix import utils) + #:use-module (guix import json) #:use-module (guix base32) #:use-module (guix hash) #:use-module (guix packages) #:use-module (guix licenses) #:use-module (guix build-system python) - #:use-module ((guix build download) #:prefix build:) #:use-module (gnu packages python) #:export (pypi->guix-package)) -(define (hash-table->alist table) - "Return an alist represenation of TABLE." - (map (match-lambda - ((key . (lst ...)) - (cons key - (map (lambda (x) - (if (hash-table? x) - (hash-table->alist x) - x)) - lst))) - ((key . (? hash-table? table)) - (cons key (hash-table->alist table))) - (pair pair)) - (hash-map->list cons table))) - -(define (flatten lst) - "Return a list that recursively concatenates all sub-lists of LIST." - (fold-right - (match-lambda* - (((sub-list ...) memo) - (append (flatten sub-list) memo)) - ((elem memo) - (cons elem memo))) - '() lst)) - (define (join lst delimiter) "Return a list that contains the elements of LST, each separated by DELIMETER." @@ -71,13 +46,6 @@ DELIMETER." ((elem . rest) (cons* elem delimiter (join rest delimiter))))) -(define (assoc-ref* alist key . rest) - "Return the value for KEY from ALIST. For each additional key specified, -recursively apply the procedure to the sub-list." - (if (null? rest) - (assoc-ref alist key) - (apply assoc-ref* (assoc-ref alist key) rest))) - (define string->license (match-lambda ("GNU LGPL" lgpl2.0) @@ -88,19 +56,6 @@ recursively apply the procedure to the sub-list." ("Apache License, Version 2.0" asl2.0) (_ #f))) -(define (url-fetch url file-name) - "Save the contents of URL to FILE-NAME. Return #f on failure." - (parameterize ((current-output-port (current-error-port))) - (build:url-fetch url file-name))) - -(define (json-fetch url) - "Return an alist representation of the JSON resource URL, or #f on failure." - (call-with-temporary-output-file - (lambda (temp port) - (and (url-fetch url temp) - (hash-table->alist - (call-with-input-file temp json->scm)))))) - (define (pypi-fetch name) "Return an alist representation of the PyPI metadata for the package NAME, or #f on failure." diff --git a/guix/import/utils.scm b/guix/import/utils.scm index 062cfc54f3..969491d28d 100644 --- a/guix/import/utils.scm +++ b/guix/import/utils.scm @@ -20,7 +20,16 @@ #:use-module (ice-9 match) #:use-module (ice-9 regex) #:use-module (srfi srfi-1) - #:export (factorize-uri)) + #:use-module (guix hash) + #:use-module (guix utils) + #:use-module ((guix build download) #:prefix build:) + #:export (factorize-uri + + hash-table->alist + flatten + assoc-ref* + + url-fetch)) (define (factorize-uri uri version) "Factorize URI, a package tarball URI as a string, such that any occurrences @@ -49,3 +58,40 @@ of the string VERSION is replaced by the symbol 'version." result)))) '() indices)))))) + +(define (hash-table->alist table) + "Return an alist represenation of TABLE." + (map (match-lambda + ((key . (lst ...)) + (cons key + (map (lambda (x) + (if (hash-table? x) + (hash-table->alist x) + x)) + lst))) + ((key . (? hash-table? table)) + (cons key (hash-table->alist table))) + (pair pair)) + (hash-map->list cons table))) + +(define (flatten lst) + "Return a list that recursively concatenates all sub-lists of LST." + (fold-right + (match-lambda* + (((sub-list ...) memo) + (append (flatten sub-list) memo)) + ((elem memo) + (cons elem memo))) + '() lst)) + +(define (assoc-ref* alist key . rest) + "Return the value for KEY from ALIST. For each additional key specified, +recursively apply the procedure to the sub-list." + (if (null? rest) + (assoc-ref alist key) + (apply assoc-ref* (assoc-ref alist key) rest))) + +(define (url-fetch url file-name) + "Save the contents of URL to FILE-NAME. Return #f on failure." + (parameterize ((current-output-port (current-error-port))) + (build:url-fetch url file-name))) diff --git a/guix/records.scm b/guix/records.scm index 93c52f0ffa..e7b86af9aa 100644 --- a/guix/records.scm +++ b/guix/records.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org> +;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org> ;;; ;;; This file is part of GNU Guix. ;;; @@ -267,15 +267,12 @@ PORT, according to FIELDS. FIELDS must be a list of field name/getter pairs." (format port "~a: ~a~%" field (get object)) (loop rest))))) -(define %recutils-field-rx - (make-regexp "^([[:graph:]]+): (.*)$")) - -(define %recutils-comment-rx - ;; info "(recutils) Comments" - (make-regexp "^#")) - -(define %recutils-plus-rx - (make-regexp "^\\+ ?(.*)$")) +(define %recutils-field-charset + ;; Valid characters starting a recutils field. + ;; info "(recutils) Fields" + (char-set-union char-set:upper-case + char-set:lower-case + (char-set #\%))) (define (recutils->alist port) "Read a recutils-style record from PORT and return it as a list of key/value @@ -288,25 +285,29 @@ pairs. Stop upon an empty line (after consuming it) or EOF." (if (null? result) (loop (read-line port) result) ; leading space: ignore it (reverse result))) ; end-of-record marker - ((regexp-exec %recutils-comment-rx line) - (loop (read-line port) result)) - ((regexp-exec %recutils-plus-rx line) - => - (lambda (m) - (match result - (((field . value) rest ...) - (loop (read-line port) - `((,field . ,(string-append value "\n" - (match:substring m 1))) - ,@rest)))))) - ((regexp-exec %recutils-field-rx line) - => - (lambda (match) - (loop (read-line port) - (alist-cons (match:substring match 1) - (match:substring match 2) - result)))) (else - (error "unmatched line" line))))) + ;; Now check the first character of LINE, since that's what the + ;; recutils manual says is enough. + (let ((first (string-ref line 0))) + (cond + ((char-set-contains? %recutils-field-charset first) + (let* ((colon (string-index line #\:)) + (field (string-take line colon)) + (value (string-trim (string-drop line (+ 1 colon))))) + (loop (read-line port) + (alist-cons field value result)))) + ((eqv? first #\#) ;info "(recutils) Comments" + (loop (read-line port) result)) + ((eqv? first #\+) ;info "(recutils) Fields" + (let ((new-line (if (string-prefix? "+ " line) + (string-drop line 2) + (string-drop line 1)))) + (match result + (((field . value) rest ...) + (loop (read-line port) + `((,field . ,(string-append value "\n" new-line)) + ,@rest)))))) + (else + (error "unmatched line" line)))))))) ;;; records.scm ends here diff --git a/guix/scripts/import.scm b/guix/scripts/import.scm index 86ef05bc2c..7e75c10b3e 100644 --- a/guix/scripts/import.scm +++ b/guix/scripts/import.scm @@ -73,7 +73,7 @@ rather than \\n." ;;; Entry point. ;;; -(define importers '("gnu" "nix" "pypi")) +(define importers '("gnu" "nix" "pypi" "cpan")) (define (resolve-importer name) (let ((module (resolve-interface diff --git a/guix/scripts/import/cpan.scm b/guix/scripts/import/cpan.scm new file mode 100644 index 0000000000..1f4dedf23f --- /dev/null +++ b/guix/scripts/import/cpan.scm @@ -0,0 +1,91 @@ +;;; GNU Guix --- Functional package management for GNU +;;; Copyright © 2014 Eric Bavier <bavier@member.fsf.org> +;;; +;;; This file is part of GNU Guix. +;;; +;;; GNU Guix is free software; you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by +;;; the Free Software Foundation; either version 3 of the License, or (at +;;; your option) any later version. +;;; +;;; GNU Guix is distributed in the hope that it will be useful, but +;;; WITHOUT ANY WARRANTY; without even the implied warranty of +;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;; GNU General Public License for more details. +;;; +;;; You should have received a copy of the GNU General Public License +;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. + +(define-module (guix scripts import cpan) + #:use-module (guix ui) + #:use-module (guix utils) + #:use-module (guix import cpan) + #:use-module (guix scripts import) + #:use-module (srfi srfi-1) + #:use-module (srfi srfi-11) + #:use-module (srfi srfi-37) + #:use-module (ice-9 match) + #:use-module (ice-9 format) + #:export (guix-import-cpan)) + + +;;; +;;; Command-line options. +;;; + +(define %default-options + '()) + +(define (show-help) + (display (_ "Usage: guix import cpan PACKAGE-NAME +Import and convert the CPAN package for PACKAGE-NAME.\n")) + (display (_ " + -h, --help display this help and exit")) + (display (_ " + -V, --version display version information and exit")) + (newline) + (show-bug-report-information)) + +(define %options + ;; Specification of the command-line options. + (cons* (option '(#\h "help") #f #f + (lambda args + (show-help) + (exit 0))) + (option '(#\V "version") #f #f + (lambda args + (show-version-and-exit "guix import cpan"))) + %standard-import-options)) + + +;;; +;;; Entry point. +;;; + +(define (guix-import-cpan . args) + (define (parse-options) + ;; Return the alist of option values. + (args-fold* args %options + (lambda (opt name arg result) + (leave (_ "~A: unrecognized option~%") name)) + (lambda (arg result) + (alist-cons 'argument arg result)) + %default-options)) + + (let* ((opts (parse-options)) + (args (filter-map (match-lambda + (('argument . value) + value) + (_ #f)) + (reverse opts)))) + (match args + ((package-name) + (let ((sexp (cpan->guix-package package-name))) + (unless sexp + (leave (_ "failed to download meta-data for package '~a'~%") + package-name)) + sexp)) + (() + (leave (_ "too few arguments~%"))) + ((many ...) + (leave (_ "too many arguments~%")))))) diff --git a/guix/scripts/substitute-binary.scm b/guix/scripts/substitute-binary.scm index 9c96411630..09b917fdf6 100755 --- a/guix/scripts/substitute-binary.scm +++ b/guix/scripts/substitute-binary.scm @@ -241,7 +241,7 @@ failure." ((version _ sig) (let ((maybe-number (string->number version))) (cond ((not (number? maybe-number)) - (leave (_ "signature version must be a number: ~a~%") + (leave (_ "signature version must be a number: ~s~%") version)) ;; Currently, there are no other versions. ((not (= 1 maybe-number)) @@ -313,18 +313,15 @@ No authentication and authorization checks are performed here!" "References" "Deriver" "System" "Signature")))) -(define %signature-line-rx - ;; Regexp matching a signature line in a narinfo. - (make-regexp "(.+)^[[:blank:]]*Signature:[[:blank:]].+$")) - (define (narinfo-sha256 narinfo) "Return the sha256 hash of NARINFO as a bytevector, or #f if NARINFO lacks a 'Signature' field." (let ((contents (narinfo-contents narinfo))) - (match (regexp-exec %signature-line-rx contents) + (match (string-contains contents "Signature:") (#f #f) - ((= (cut match:substring <> 1) above-signature) - (sha256 (string->utf8 above-signature)))))) + (index + (let ((above-signature (string-take contents index))) + (sha256 (string->utf8 above-signature))))))) (define* (assert-valid-narinfo narinfo #:optional (acl (current-acl)) diff --git a/guix/tests.scm b/guix/tests.scm index 82ae7e2084..36341cb4cc 100644 --- a/guix/tests.scm +++ b/guix/tests.scm @@ -27,6 +27,7 @@ #:export (open-connection-for-tests random-text random-bytevector + mock with-derivation-narinfo dummy-package)) @@ -70,6 +71,16 @@ (loop (1+ i))) bv)))) +(define-syntax-rule (mock (module proc replacement) body ...) + "Within BODY, replace the definition of PROC from MODULE with the definition +given by REPLACEMENT." + (let* ((m (resolve-module 'module)) + (original (module-ref m 'proc))) + (dynamic-wind + (lambda () (module-set! m 'proc replacement)) + (lambda () body ...) + (lambda () (module-set! m 'proc original))))) + ;;; ;;; Narinfo files, as used by the substituter. diff --git a/guix/ui.scm b/guix/ui.scm index c77e04172e..5bd4d1f8c2 100644 --- a/guix/ui.scm +++ b/guix/ui.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org> +;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org> ;;; Copyright © 2013 Mark H Weaver <mhw@netris.org> ;;; Copyright © 2013 Nikita Karetnikov <nikita@karetnikov.org> ;;; Copyright © 2014 Alex Kost <alezost@gmail.com> @@ -299,21 +299,27 @@ error." derivations listed in DRV. Return #t if there's something to build, #f otherwise. When USE-SUBSTITUTES?, check and report what is prerequisites are available for download." + (define substitutable? + ;; Call 'substitutation-oracle' upfront so we don't end up launching the + ;; substituter many times. This makes a big difference, especially when + ;; DRV is a long list as is the case with 'guix environment'. + (if use-substitutes? + (substitution-oracle store drv) + (const #f))) + (define (built-or-substitutable? drv) (let ((out (derivation->output-path drv))) ;; If DRV has zero outputs, OUT is #f. (or (not out) (or (valid-path? store out) - (and use-substitutes? - (has-substitutes? store out)))))) + (substitutable? out))))) (let*-values (((build download) (fold2 (lambda (drv build download) (let-values (((b d) (derivation-prerequisites-to-build store drv - #:use-substitutes? - use-substitutes?))) + #:substitutable? substitutable?))) (values (append b build) (append d download)))) '() '() |