guix build: '--with-commit' makes recursive checkouts.
[jackhill/guix/guix.git] / guix / scripts / archive.scm
index 8280a82..d349b5d 100644 (file)
@@ -1,5 +1,5 @@
 ;;; GNU Guix --- Functional package management for GNU
-;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
+;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2019 Ludovic Courtès <ludo@gnu.org>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
 (define-module (guix scripts archive)
   #:use-module (guix config)
   #:use-module (guix utils)
+  #:use-module (guix combinators)
   #:use-module ((guix build utils) #:select (mkdir-p))
+  #:use-module ((guix serialization) #:select (restore-file))
   #:use-module (guix store)
+  #:use-module ((guix status) #:select (with-status-verbosity))
+  #:use-module (guix grafts)
   #:use-module (guix packages)
   #:use-module (guix derivations)
+  #:use-module (guix monads)
   #:use-module (guix ui)
   #:use-module (guix pki)
-  #:use-module (guix pk-crypto)
+  #:use-module (gcrypt pk-crypto)
+  #:use-module (guix scripts)
+  #:use-module (guix scripts build)
+  #:use-module (gnu packages)
   #:use-module (ice-9 match)
   #:use-module (ice-9 format)
   #:use-module (ice-9 rdelim)
   #:use-module (srfi srfi-11)
   #:use-module (srfi srfi-26)
   #:use-module (srfi srfi-37)
-  #:use-module (guix scripts build)
-  #:use-module (guix scripts package)
-  #:use-module (rnrs io ports)
-  #:export (guix-archive))
+  #:use-module (ice-9 binary-ports)
+  #:export (guix-archive
+            options->derivations+files))
 
 \f
 ;;;
   ;; Alist of default option values.
   `((system . ,(%current-system))
     (substitutes? . #t)
-    (max-silent-time . 3600)
-    (verbosity . 0)))
+    (build-hook? . #t)
+    (graft? . #t)
+    (print-build-trace? . #t)
+    (print-extended-build-trace? . #t)
+    (multiplexed-build-output? . #t)
+    (verbosity . 2)
+    (debug . 0)))
 
 (define (show-help)
-  (display (_ "Usage: guix archive [OPTION]... PACKAGE...
+  (display (G_ "Usage: guix archive [OPTION]... PACKAGE...
 Export/import one or more packages from/to the store.\n"))
-  (display (_ "
+  (display (G_ "
       --export           export the specified files/packages to stdout"))
-  (display (_ "
+  (display (G_ "
+  -r, --recursive        combined with '--export', include dependencies"))
+  (display (G_ "
       --import           import from the archive passed on stdin"))
-  (display (_ "
+  (display (G_ "
       --missing          print the files from stdin that are missing"))
+  (display (G_ "
+  -x, --extract=DIR      extract the archive on stdin to DIR"))
   (newline)
-  (display (_ "
+  (display (G_ "
       --generate-key[=PARAMETERS]
                          generate a key pair with the given parameters"))
-  (display (_ "
+  (display (G_ "
       --authorize        authorize imports signed by the public key on stdin"))
   (newline)
-  (display (_ "
+  (display (G_ "
   -e, --expression=EXPR  build the package or derivation EXPR evaluates to"))
-  (display (_ "
+  (display (G_ "
   -S, --source           build the packages' source derivations"))
-  (display (_ "
+  (display (G_ "
   -s, --system=SYSTEM    attempt to build for SYSTEM--e.g., \"i686-linux\""))
-  (display (_ "
+  (display (G_ "
       --target=TRIPLET   cross-build for TRIPLET--e.g., \"armel-linux-gnu\""))
+  (display (G_ "
+  -v, --verbosity=LEVEL  use the given verbosity LEVEL"))
 
   (newline)
   (show-build-options-help)
 
   (newline)
-  (display (_ "
+  (display (G_ "
   -h, --help             display this help and exit"))
-  (display (_ "
+  (display (G_ "
   -V, --version          display version information and exit"))
   (newline)
   (show-bug-report-information))
 
+(define %key-generation-parameters
+  ;; Default key generation parameters.  We prefer Ed25519, but it was
+  ;; introduced in libgcrypt 1.6.0.
+  (if (version>? (gcrypt-version) "1.6.0")
+      "(genkey (ecdsa (curve Ed25519) (flags rfc6979)))"
+      "(genkey (rsa (nbits 4:4096)))"))
+
 (define %options
   ;; Specifications of the command-line options.
   (cons* (option '(#\h "help") #f #f
@@ -99,23 +124,32 @@ Export/import one or more packages from/to the store.\n"))
          (option '("export") #f #f
                  (lambda (opt name arg result)
                    (alist-cons 'export #t result)))
+         (option '(#\r "recursive") #f #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'export-recursive? #t result)))
          (option '("import") #f #f
                  (lambda (opt name arg result)
                    (alist-cons 'import #t result)))
          (option '("missing") #f #f
                  (lambda (opt name arg result)
                    (alist-cons 'missing #t result)))
+         (option '("extract" #\x) #t #f
+                 (lambda (opt name arg result)
+                   (alist-cons 'extract arg result)))
          (option '("generate-key") #f #t
                  (lambda (opt name arg result)
                    (catch 'gcry-error
                      (lambda ()
+                       ;; XXX: Curve25519 was actually introduced in
+                       ;; libgcrypt 1.6.0.
                        (let ((params
                               (string->canonical-sexp
-                               (or arg "(genkey (rsa (nbits 4:4096)))"))))
+                               (or arg %key-generation-parameters))))
                          (alist-cons 'generate-key params result)))
-                     (lambda args
-                       (leave (_ "invalid key generation parameters: ~s~%")
-                              arg)))))
+                     (lambda (key proc err)
+                       (leave (G_ "invalid key generation parameters: ~a: ~a~%")
+                              (error-source err)
+                              (error-string err))))))
          (option '("authorize") #f #f
                  (lambda (opt name arg result)
                    (alist-cons 'authorize #t result)))
@@ -134,15 +168,38 @@ Export/import one or more packages from/to the store.\n"))
          (option '(#\e "expression") #t #f
                  (lambda (opt name arg result)
                    (alist-cons 'expression arg result)))
-         (option '(#\n "dry-run") #f #f
+         (option '(#\v "verbosity") #t #f
                  (lambda (opt name arg result)
-                   (alist-cons 'dry-run? #t result)))
-         (option '(#\r "root") #t #f
+                   (let ((level (string->number* arg)))
+                     (alist-cons 'verbosity level
+                                 (alist-delete 'verbosity result)))))
+         (option '(#\n "dry-run") #f #f
                  (lambda (opt name arg result)
-                   (alist-cons 'gc-root arg result)))
+                   (alist-cons 'dry-run? #t (alist-cons 'graft? #f result))))
 
          %standard-build-options))
 
+(define (derivation-from-expression store str package-derivation
+                                    system source?)
+  "Read/eval STR and return the corresponding derivation path for SYSTEM.
+When SOURCE? is true and STR evaluates to a package, return the derivation of
+the package source; otherwise, use PACKAGE-DERIVATION to compute the
+derivation of a package."
+  (match (read/eval str)
+    ((? package? p)
+     (if source?
+         (let ((source (package-source p)))
+           (if source
+               (package-source-derivation store source)
+               (leave (G_ "package `~a' has no source~%")
+                      (package-name p))))
+         (package-derivation store p system)))
+    ((? procedure? proc)
+     (run-with-store store
+       (mbegin %store-monad
+         (set-guile-for-build (default-guile))
+         (proc)) #:system system))))
+
 (define (options->derivations+files store opts)
   "Given OPTS, the result of 'args-fold', return a list of derivations to
 build and a list of store files to transfer."
@@ -194,33 +251,33 @@ build and a list of store files to transfer."
 resulting archive to the standard output port."
   (let-values (((drv files)
                 (options->derivations+files store opts)))
-    (set-build-options-from-command-line store opts)
     (show-what-to-build store drv
                         #:use-substitutes? (assoc-ref opts 'substitutes?)
                         #:dry-run? (assoc-ref opts 'dry-run?))
 
     (if (or (assoc-ref opts 'dry-run?)
             (build-derivations store drv))
-        (export-paths store files (current-output-port))
-        (leave (_ "unable to export the given packages~%")))))
+        (export-paths store files (current-output-port)
+                      #:recursive? (assoc-ref opts 'export-recursive?))
+        (leave (G_ "unable to export the given packages~%")))))
 
 (define (generate-key-pair parameters)
   "Generate a key pair with PARAMETERS, a canonical sexp, and store it in the
 right place."
   (when (or (file-exists? %public-key-file)
             (file-exists? %private-key-file))
-    (leave (_ "key pair exists under '~a'; remove it first~%")
+    (leave (G_ "key pair exists under '~a'; remove it first~%")
            (dirname %public-key-file)))
 
   (format (current-error-port)
-          (_ "Please wait while gathering entropy to generate the key pair;
+          (G_ "Please wait while gathering entropy to generate the key pair;
 this may take time...~%"))
 
   (let* ((pair   (catch 'gcry-error
                    (lambda ()
                      (generate-key parameters))
-                   (lambda (key err)
-                     (leave (_ "key generation failed: ~a: ~a~%")
+                   (lambda (key proc err)
+                     (leave (G_ "key generation failed: ~a: ~a~%")
                             (error-source err)
                             (error-string err)))))
          (public (find-sexp-token pair 'public-key))
@@ -245,32 +302,23 @@ the input port."
   (define (read-key)
     (catch 'gcry-error
       (lambda ()
-        (string->canonical-sexp (get-string-all (current-input-port))))
-      (lambda (key err)
-        (leave (_ "failed to read public key: ~a: ~a~%")
+        (string->canonical-sexp (read-string (current-input-port))))
+      (lambda (key proc err)
+        (leave (G_ "failed to read public key: ~a: ~a~%")
                (error-source err) (error-string err)))))
 
   (let ((key (read-key))
         (acl (current-acl)))
     (unless (eq? 'public-key (canonical-sexp-nth-data key 0))
-      (leave (_ "s-expression does not denote a public key~%")))
+      (leave (G_ "s-expression does not denote a public key~%")))
 
     ;; Add KEY to the ACL and write that.
     (let ((acl (public-keys->acl (cons key (acl->public-keys acl)))))
+      (mkdir-p (dirname %acl-file))
       (with-atomic-file-output %acl-file
-        (lambda (port)
-          (display (canonical-sexp->string acl) port))))))
+        (cut write-acl acl <>)))))
 
 (define (guix-archive . 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))
-
   (define (lines port)
     ;; Return lines read from PORT.
     (let loop ((line   (read-line port))
@@ -284,24 +332,31 @@ the input port."
     ;; Ask for absolute file names so that .drv file names passed from the
     ;; user to 'read-derivation' are absolute when it returns.
     (with-fluids ((%file-port-name-canonicalization 'absolute))
-      (let ((opts (parse-options)))
-        (cond ((assoc-ref opts 'generate-key)
-               =>
-               generate-key-pair)
-              ((assoc-ref opts 'authorize)
-               (authorize-key))
-              (else
-               (let ((store (open-connection)))
-                 (cond ((assoc-ref opts 'export)
-                        (export-from-store store opts))
-                       ((assoc-ref opts 'import)
-                        (import-paths store (current-input-port)))
-                       ((assoc-ref opts 'missing)
-                        (let* ((files   (lines (current-input-port)))
-                               (missing (remove (cut valid-path? store <>)
-                                                files)))
-                          (format #t "~{~a~%~}" missing)))
-                       (else
-                        (leave
-                         (_ "either '--export' or '--import' \
-must be specified~%")))))))))))
+      (let ((opts (parse-command-line args %options (list %default-options))))
+        (parameterize ((%graft? (assoc-ref opts 'graft?)))
+          (cond ((assoc-ref opts 'generate-key)
+                 =>
+                 generate-key-pair)
+                ((assoc-ref opts 'authorize)
+                 (authorize-key))
+                (else
+                 (with-status-verbosity (assoc-ref opts 'verbosity)
+                   (with-store store
+                     (set-build-options-from-command-line store opts)
+                     (cond ((assoc-ref opts 'export)
+                            (export-from-store store opts))
+                           ((assoc-ref opts 'import)
+                            (import-paths store (current-input-port)))
+                           ((assoc-ref opts 'missing)
+                            (let* ((files   (lines (current-input-port)))
+                                   (missing (remove (cut valid-path? store <>)
+                                                    files)))
+                              (format #t "~{~a~%~}" missing)))
+                           ((assoc-ref opts 'extract)
+                            =>
+                            (lambda (target)
+                              (restore-file (current-input-port) target)))
+                           (else
+                            (leave
+                             (G_ "either '--export' or '--import' \
+must be specified~%")))))))))))))