graph: Use 'derivation-input-derivation'.
[jackhill/guix/guix.git] / guix / scripts / archive.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2013, 2014, 2015, 2016, 2017, 2019 Ludovic Courtès <ludo@gnu.org>
3 ;;;
4 ;;; This file is part of GNU Guix.
5 ;;;
6 ;;; GNU Guix is free software; you can redistribute it and/or modify it
7 ;;; under the terms of the GNU General Public License as published by
8 ;;; the Free Software Foundation; either version 3 of the License, or (at
9 ;;; your option) any later version.
10 ;;;
11 ;;; GNU Guix is distributed in the hope that it will be useful, but
12 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ;;; GNU General Public License for more details.
15 ;;;
16 ;;; You should have received a copy of the GNU General Public License
17 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19 (define-module (guix scripts archive)
20 #:use-module (guix config)
21 #:use-module (guix utils)
22 #:use-module (guix combinators)
23 #:use-module ((guix build utils) #:select (mkdir-p))
24 #:use-module ((guix serialization) #:select (restore-file))
25 #:use-module (guix store)
26 #:use-module ((guix status) #:select (with-status-verbosity))
27 #:use-module (guix grafts)
28 #:use-module (guix packages)
29 #:use-module (guix derivations)
30 #:use-module (guix monads)
31 #:use-module (guix ui)
32 #:use-module (guix pki)
33 #:use-module (gcrypt pk-crypto)
34 #:use-module (guix scripts)
35 #:use-module (guix scripts build)
36 #:use-module (gnu packages)
37 #:use-module (ice-9 match)
38 #:use-module (ice-9 format)
39 #:use-module (ice-9 rdelim)
40 #:use-module (srfi srfi-1)
41 #:use-module (srfi srfi-11)
42 #:use-module (srfi srfi-26)
43 #:use-module (srfi srfi-37)
44 #:use-module (ice-9 binary-ports)
45 #:export (guix-archive
46 options->derivations+files))
47
48 \f
49 ;;;
50 ;;; Command-line options.
51 ;;;
52
53 (define %default-options
54 ;; Alist of default option values.
55 `((system . ,(%current-system))
56 (substitutes? . #t)
57 (build-hook? . #t)
58 (graft? . #t)
59 (print-build-trace? . #t)
60 (print-extended-build-trace? . #t)
61 (multiplexed-build-output? . #t)
62 (verbosity . 2)
63 (debug . 0)))
64
65 (define (show-help)
66 (display (G_ "Usage: guix archive [OPTION]... PACKAGE...
67 Export/import one or more packages from/to the store.\n"))
68 (display (G_ "
69 --export export the specified files/packages to stdout"))
70 (display (G_ "
71 -r, --recursive combined with '--export', include dependencies"))
72 (display (G_ "
73 --import import from the archive passed on stdin"))
74 (display (G_ "
75 --missing print the files from stdin that are missing"))
76 (display (G_ "
77 -x, --extract=DIR extract the archive on stdin to DIR"))
78 (newline)
79 (display (G_ "
80 --generate-key[=PARAMETERS]
81 generate a key pair with the given parameters"))
82 (display (G_ "
83 --authorize authorize imports signed by the public key on stdin"))
84 (newline)
85 (display (G_ "
86 -e, --expression=EXPR build the package or derivation EXPR evaluates to"))
87 (display (G_ "
88 -S, --source build the packages' source derivations"))
89 (display (G_ "
90 -s, --system=SYSTEM attempt to build for SYSTEM--e.g., \"i686-linux\""))
91 (display (G_ "
92 --target=TRIPLET cross-build for TRIPLET--e.g., \"armel-linux-gnu\""))
93 (display (G_ "
94 -v, --verbosity=LEVEL use the given verbosity LEVEL"))
95
96 (newline)
97 (show-build-options-help)
98
99 (newline)
100 (display (G_ "
101 -h, --help display this help and exit"))
102 (display (G_ "
103 -V, --version display version information and exit"))
104 (newline)
105 (show-bug-report-information))
106
107 (define %key-generation-parameters
108 ;; Default key generation parameters. We prefer Ed25519, but it was
109 ;; introduced in libgcrypt 1.6.0.
110 (if (version>? (gcrypt-version) "1.6.0")
111 "(genkey (ecdsa (curve Ed25519) (flags rfc6979)))"
112 "(genkey (rsa (nbits 4:4096)))"))
113
114 (define %options
115 ;; Specifications of the command-line options.
116 (cons* (option '(#\h "help") #f #f
117 (lambda args
118 (show-help)
119 (exit 0)))
120 (option '(#\V "version") #f #f
121 (lambda args
122 (show-version-and-exit "guix build")))
123
124 (option '("export") #f #f
125 (lambda (opt name arg result)
126 (alist-cons 'export #t result)))
127 (option '(#\r "recursive") #f #f
128 (lambda (opt name arg result)
129 (alist-cons 'export-recursive? #t result)))
130 (option '("import") #f #f
131 (lambda (opt name arg result)
132 (alist-cons 'import #t result)))
133 (option '("missing") #f #f
134 (lambda (opt name arg result)
135 (alist-cons 'missing #t result)))
136 (option '("extract" #\x) #t #f
137 (lambda (opt name arg result)
138 (alist-cons 'extract arg result)))
139 (option '("generate-key") #f #t
140 (lambda (opt name arg result)
141 (catch 'gcry-error
142 (lambda ()
143 ;; XXX: Curve25519 was actually introduced in
144 ;; libgcrypt 1.6.0.
145 (let ((params
146 (string->canonical-sexp
147 (or arg %key-generation-parameters))))
148 (alist-cons 'generate-key params result)))
149 (lambda (key proc err)
150 (leave (G_ "invalid key generation parameters: ~a: ~a~%")
151 (error-source err)
152 (error-string err))))))
153 (option '("authorize") #f #f
154 (lambda (opt name arg result)
155 (alist-cons 'authorize #t result)))
156
157 (option '(#\S "source") #f #f
158 (lambda (opt name arg result)
159 (alist-cons 'source? #t result)))
160 (option '(#\s "system") #t #f
161 (lambda (opt name arg result)
162 (alist-cons 'system arg
163 (alist-delete 'system result eq?))))
164 (option '("target") #t #f
165 (lambda (opt name arg result)
166 (alist-cons 'target arg
167 (alist-delete 'target result eq?))))
168 (option '(#\e "expression") #t #f
169 (lambda (opt name arg result)
170 (alist-cons 'expression arg result)))
171 (option '(#\v "verbosity") #t #f
172 (lambda (opt name arg result)
173 (let ((level (string->number* arg)))
174 (alist-cons 'verbosity level
175 (alist-delete 'verbosity result)))))
176 (option '(#\n "dry-run") #f #f
177 (lambda (opt name arg result)
178 (alist-cons 'dry-run? #t (alist-cons 'graft? #f result))))
179
180 %standard-build-options))
181
182 (define (derivation-from-expression store str package-derivation
183 system source?)
184 "Read/eval STR and return the corresponding derivation path for SYSTEM.
185 When SOURCE? is true and STR evaluates to a package, return the derivation of
186 the package source; otherwise, use PACKAGE-DERIVATION to compute the
187 derivation of a package."
188 (match (read/eval str)
189 ((? package? p)
190 (if source?
191 (let ((source (package-source p)))
192 (if source
193 (package-source-derivation store source)
194 (leave (G_ "package `~a' has no source~%")
195 (package-name p))))
196 (package-derivation store p system)))
197 ((? procedure? proc)
198 (run-with-store store
199 (mbegin %store-monad
200 (set-guile-for-build (default-guile))
201 (proc)) #:system system))))
202
203 (define (options->derivations+files store opts)
204 "Given OPTS, the result of 'args-fold', return a list of derivations to
205 build and a list of store files to transfer."
206 (define package->derivation
207 (match (assoc-ref opts 'target)
208 (#f package-derivation)
209 (triplet
210 (cut package-cross-derivation <> <> triplet <>))))
211
212 (define src? (assoc-ref opts 'source?))
213 (define sys (assoc-ref opts 'system))
214
215 (fold2 (lambda (arg derivations files)
216 (match arg
217 (('expression . str)
218 (let ((drv (derivation-from-expression store str
219 package->derivation
220 sys src?)))
221 (values (cons drv derivations)
222 (cons (derivation->output-path drv) files))))
223 (('argument . (? store-path? file))
224 (values derivations (cons file files)))
225 (('argument . (? string? spec))
226 (let-values (((p output)
227 (specification->package+output spec)))
228 (if src?
229 (let* ((s (package-source p))
230 (drv (package-source-derivation store s)))
231 (values (cons drv derivations)
232 (cons (derivation->output-path drv)
233 files)))
234 (let ((drv (package->derivation store p sys)))
235 (values (cons drv derivations)
236 (cons (derivation->output-path drv output)
237 files))))))
238 (_
239 (values derivations files))))
240 '()
241 '()
242 opts))
243
244 \f
245 ;;;
246 ;;; Entry point.
247 ;;;
248
249 (define (export-from-store store opts)
250 "Export the packages or derivations specified in OPTS from STORE. Write the
251 resulting archive to the standard output port."
252 (let-values (((drv files)
253 (options->derivations+files store opts)))
254 (show-what-to-build store drv
255 #:use-substitutes? (assoc-ref opts 'substitutes?)
256 #:dry-run? (assoc-ref opts 'dry-run?))
257
258 (if (or (assoc-ref opts 'dry-run?)
259 (build-derivations store drv))
260 (export-paths store files (current-output-port)
261 #:recursive? (assoc-ref opts 'export-recursive?))
262 (leave (G_ "unable to export the given packages~%")))))
263
264 (define (generate-key-pair parameters)
265 "Generate a key pair with PARAMETERS, a canonical sexp, and store it in the
266 right place."
267 (when (or (file-exists? %public-key-file)
268 (file-exists? %private-key-file))
269 (leave (G_ "key pair exists under '~a'; remove it first~%")
270 (dirname %public-key-file)))
271
272 (format (current-error-port)
273 (G_ "Please wait while gathering entropy to generate the key pair;
274 this may take time...~%"))
275
276 (let* ((pair (catch 'gcry-error
277 (lambda ()
278 (generate-key parameters))
279 (lambda (key proc err)
280 (leave (G_ "key generation failed: ~a: ~a~%")
281 (error-source err)
282 (error-string err)))))
283 (public (find-sexp-token pair 'public-key))
284 (secret (find-sexp-token pair 'private-key)))
285 ;; Create the following files as #o400.
286 (umask #o266)
287
288 (mkdir-p (dirname %public-key-file))
289 (with-atomic-file-output %public-key-file
290 (lambda (port)
291 (display (canonical-sexp->string public) port)))
292 (with-atomic-file-output %private-key-file
293 (lambda (port)
294 (display (canonical-sexp->string secret) port)))
295
296 ;; Make the public key readable by everyone.
297 (chmod %public-key-file #o444)))
298
299 (define (authorize-key)
300 "Authorize imports signed by the public key passed as an advanced sexp on
301 the input port."
302 (define (read-key)
303 (catch 'gcry-error
304 (lambda ()
305 (string->canonical-sexp (read-string (current-input-port))))
306 (lambda (key proc err)
307 (leave (G_ "failed to read public key: ~a: ~a~%")
308 (error-source err) (error-string err)))))
309
310 (let ((key (read-key))
311 (acl (current-acl)))
312 (unless (eq? 'public-key (canonical-sexp-nth-data key 0))
313 (leave (G_ "s-expression does not denote a public key~%")))
314
315 ;; Add KEY to the ACL and write that.
316 (let ((acl (public-keys->acl (cons key (acl->public-keys acl)))))
317 (mkdir-p (dirname %acl-file))
318 (with-atomic-file-output %acl-file
319 (cut write-acl acl <>)))))
320
321 (define (guix-archive . args)
322 (define (lines port)
323 ;; Return lines read from PORT.
324 (let loop ((line (read-line port))
325 (result '()))
326 (if (eof-object? line)
327 (reverse result)
328 (loop (read-line port)
329 (cons line result)))))
330
331 (with-error-handling
332 ;; Ask for absolute file names so that .drv file names passed from the
333 ;; user to 'read-derivation' are absolute when it returns.
334 (with-fluids ((%file-port-name-canonicalization 'absolute))
335 (let ((opts (parse-command-line args %options (list %default-options))))
336 (parameterize ((%graft? (assoc-ref opts 'graft?)))
337 (cond ((assoc-ref opts 'generate-key)
338 =>
339 generate-key-pair)
340 ((assoc-ref opts 'authorize)
341 (authorize-key))
342 (else
343 (with-status-verbosity (assoc-ref opts 'verbosity)
344 (with-store store
345 (set-build-options-from-command-line store opts)
346 (cond ((assoc-ref opts 'export)
347 (export-from-store store opts))
348 ((assoc-ref opts 'import)
349 (import-paths store (current-input-port)))
350 ((assoc-ref opts 'missing)
351 (let* ((files (lines (current-input-port)))
352 (missing (remove (cut valid-path? store <>)
353 files)))
354 (format #t "~{~a~%~}" missing)))
355 ((assoc-ref opts 'extract)
356 =>
357 (lambda (target)
358 (restore-file (current-input-port) target)))
359 (else
360 (leave
361 (G_ "either '--export' or '--import' \
362 must be specified~%")))))))))))))