check-available-binaries: Use 'substitutable-paths'.
[guix.git] / guix / scripts / build.scm
blobd593b5a8a701dbe45fe8a4959bbafb14baab2eec
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2012, 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2013 Mark H Weaver <mhw@netris.org>
4 ;;;
5 ;;; This file is part of GNU Guix.
6 ;;;
7 ;;; GNU Guix is free software; you can redistribute it and/or modify it
8 ;;; under the terms of the GNU General Public License as published by
9 ;;; the Free Software Foundation; either version 3 of the License, or (at
10 ;;; your option) any later version.
11 ;;;
12 ;;; GNU Guix is distributed in the hope that it will be useful, but
13 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ;;; GNU General Public License for more details.
16 ;;;
17 ;;; You should have received a copy of the GNU General Public License
18 ;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
20 (define-module (guix scripts build)
21   #:use-module (guix ui)
22   #:use-module (guix store)
23   #:use-module (guix derivations)
24   #:use-module (guix packages)
25   #:use-module (guix utils)
26   #:use-module (guix monads)
27   #:use-module (guix gexp)
28   #:use-module (ice-9 format)
29   #:use-module (ice-9 match)
30   #:use-module (ice-9 vlist)
31   #:use-module (srfi srfi-1)
32   #:use-module (srfi srfi-11)
33   #:use-module (srfi srfi-26)
34   #:use-module (srfi srfi-34)
35   #:use-module (srfi srfi-37)
36   #:autoload   (gnu packages) (specification->package %package-module-path)
37   #:autoload   (guix download) (download-to-store)
38   #:export (%standard-build-options
39             set-build-options-from-command-line
40             set-build-options-from-command-line*
41             show-build-options-help
43             guix-build))
45 (define (register-root store paths root)
46   "Register ROOT as an indirect GC root for all of PATHS."
47   (let* ((root (string-append (canonicalize-path (dirname root))
48                               "/" root)))
49     (catch 'system-error
50       (lambda ()
51         (match paths
52           ((path)
53            (symlink path root)
54            (add-indirect-root store root))
55           ((paths ...)
56            (fold (lambda (path count)
57                    (let ((root (string-append root
58                                               "-"
59                                               (number->string count))))
60                      (symlink path root)
61                      (add-indirect-root store root))
62                    (+ 1 count))
63                  0
64                  paths))))
65       (lambda args
66         (leave (_ "failed to create GC root `~a': ~a~%")
67                root (strerror (system-error-errno args)))))))
69 (define (package-with-source store p uri)
70   "Return a package based on P but with its source taken from URI.  Extract
71 the new package's version number from URI."
72   (define (numeric-extension? file-name)
73     ;; Return true if FILE-NAME ends with digits.
74     (string-every char-set:hex-digit (file-extension file-name)))
76   (define (tarball-base-name file-name)
77     ;; Return the "base" of FILE-NAME, removing '.tar.gz' or similar
78     ;; extensions.
79     ;; TODO: Factorize.
80     (cond ((not (file-extension file-name))
81            file-name)
82           ((numeric-extension? file-name)
83            file-name)
84           ((string=? (file-extension file-name) "tar")
85            (file-sans-extension file-name))
86           ((file-extension file-name)
87            (tarball-base-name (file-sans-extension file-name)))
88           (else
89            file-name)))
91   (let ((base (tarball-base-name (basename uri))))
92     (let-values (((name version)
93                   (package-name->name+version base)))
94       (package (inherit p)
95                (version (or version (package-version p)))
97                ;; Use #:recursive? #t to allow for directories.
98                (source (download-to-store store uri
99                                           #:recursive? #t))))))
103 ;;; Standard command-line build options.
106 (define (show-build-options-help)
107   "Display on the current output port help about the standard command-line
108 options handled by 'set-build-options-from-command-line', and listed in
109 '%standard-build-options'."
110   (display (_ "
111   -L, --load-path=DIR    prepend DIR to the package module search path"))
112   (display (_ "
113   -K, --keep-failed      keep build tree of failed builds"))
114   (display (_ "
115   -n, --dry-run          do not build the derivations"))
116   (display (_ "
117       --fallback         fall back to building when the substituter fails"))
118   (display (_ "
119       --no-substitutes   build instead of resorting to pre-built substitutes"))
120   (display (_ "
121       --substitute-urls=URLS
122                          fetch substitute from URLS if they are authorized"))
123   (display (_ "
124       --no-build-hook    do not attempt to offload builds via the build hook"))
125   (display (_ "
126       --max-silent-time=SECONDS
127                          mark the build as failed after SECONDS of silence"))
128   (display (_ "
129       --timeout=SECONDS  mark the build as failed after SECONDS of activity"))
130   (display (_ "
131       --verbosity=LEVEL  use the given verbosity LEVEL"))
132   (display (_ "
133   -c, --cores=N          allow the use of up to N CPU cores for the build"))
134   (display (_ "
135   -M, --max-jobs=N       allow at most N build jobs")))
137 (define (set-build-options-from-command-line store opts)
138   "Given OPTS, an alist as returned by 'args-fold' given
139 '%standard-build-options', set the corresponding build options on STORE."
140   ;; TODO: Add more options.
141   (set-build-options store
142                      #:keep-failed? (assoc-ref opts 'keep-failed?)
143                      #:build-cores (or (assoc-ref opts 'cores) 0)
144                      #:max-build-jobs (or (assoc-ref opts 'max-jobs) 1)
145                      #:fallback? (assoc-ref opts 'fallback?)
146                      #:use-substitutes? (assoc-ref opts 'substitutes?)
147                      #:substitute-urls (or (assoc-ref opts 'substitute-urls)
148                                            %default-substitute-urls)
149                      #:use-build-hook? (assoc-ref opts 'build-hook?)
150                      #:max-silent-time (assoc-ref opts 'max-silent-time)
151                      #:timeout (assoc-ref opts 'timeout)
152                      #:print-build-trace (assoc-ref opts 'print-build-trace?)
153                      #:verbosity (assoc-ref opts 'verbosity)))
155 (define set-build-options-from-command-line*
156   (store-lift set-build-options-from-command-line))
158 (define %standard-build-options
159   ;; List of standard command-line options for tools that build something.
160   (list (option '(#\L "load-path") #t #f
161                 (lambda (opt name arg result . rest)
162                   ;; XXX: Imperatively modify the search paths.
163                   (%package-module-path (cons arg (%package-module-path)))
164                   (set! %load-path (cons arg %load-path))
165                   (set! %load-compiled-path (cons arg %load-compiled-path))
167                   (apply values (cons result rest))))
168         (option '(#\K "keep-failed") #f #f
169                 (lambda (opt name arg result . rest)
170                   (apply values
171                          (alist-cons 'keep-failed? #t result)
172                          rest)))
173         (option '("fallback") #f #f
174                 (lambda (opt name arg result . rest)
175                   (apply values
176                          (alist-cons 'fallback? #t
177                                      (alist-delete 'fallback? result))
178                          rest)))
179         (option '("no-substitutes") #f #f
180                 (lambda (opt name arg result . rest)
181                   (apply values
182                          (alist-cons 'substitutes? #f
183                                      (alist-delete 'substitutes? result))
184                          rest)))
185         (option '("substitute-urls") #t #f
186                 (lambda (opt name arg result . rest)
187                   (apply values
188                          (alist-cons 'substitute-urls
189                                      (string-tokenize arg)
190                                      (alist-delete 'substitute-urls result))
191                          rest)))
192         (option '("no-build-hook") #f #f
193                 (lambda (opt name arg result . rest)
194                   (apply values
195                          (alist-cons 'build-hook? #f
196                                      (alist-delete 'build-hook? result))
197                          rest)))
198         (option '("max-silent-time") #t #f
199                 (lambda (opt name arg result . rest)
200                   (apply values
201                          (alist-cons 'max-silent-time (string->number* arg)
202                                      result)
203                          rest)))
204         (option '("timeout") #t #f
205                 (lambda (opt name arg result . rest)
206                   (apply values
207                          (alist-cons 'timeout (string->number* arg) result)
208                          rest)))
209         (option '("verbosity") #t #f
210                 (lambda (opt name arg result . rest)
211                   (let ((level (string->number arg)))
212                     (apply values
213                            (alist-cons 'verbosity level
214                                        (alist-delete 'verbosity result))
215                            rest))))
216         (option '(#\c "cores") #t #f
217                 (lambda (opt name arg result . rest)
218                   (let ((c (false-if-exception (string->number arg))))
219                     (if c
220                         (apply values (alist-cons 'cores c result) rest)
221                         (leave (_ "not a number: '~a' option argument: ~a~%")
222                                name arg)))))
223         (option '(#\M "max-jobs") #t #f
224                 (lambda (opt name arg result . rest)
225                   (let ((c (false-if-exception (string->number arg))))
226                     (if c
227                         (apply values (alist-cons 'max-jobs c result) rest)
228                         (leave (_ "not a number: '~a' option argument: ~a~%")
229                                name arg)))))))
233 ;;; Command-line options.
236 (define %default-options
237   ;; Alist of default option values.
238   `((system . ,(%current-system))
239     (graft? . #t)
240     (substitutes? . #t)
241     (build-hook? . #t)
242     (print-build-trace? . #t)
243     (max-silent-time . 3600)
244     (verbosity . 0)))
246 (define (show-help)
247   (display (_ "Usage: guix build [OPTION]... PACKAGE-OR-DERIVATION...
248 Build the given PACKAGE-OR-DERIVATION and return their output paths.\n"))
249   (display (_ "
250   -e, --expression=EXPR  build the package or derivation EXPR evaluates to"))
251   (display (_ "
252   -S, --source           build the packages' source derivations"))
253   (display (_ "
254       --sources[=TYPE]   build source derivations; TYPE may optionally be one
255                          of \"package\", \"all\" (default), or \"transitive\""))
256   (display (_ "
257   -s, --system=SYSTEM    attempt to build for SYSTEM--e.g., \"i686-linux\""))
258   (display (_ "
259       --target=TRIPLET   cross-build for TRIPLET--e.g., \"armel-linux-gnu\""))
260   (display (_ "
261       --with-source=SOURCE
262                          use SOURCE when building the corresponding package"))
263   (display (_ "
264       --no-grafts        do not graft packages"))
265   (display (_ "
266   -d, --derivations      return the derivation paths of the given packages"))
267   (display (_ "
268   -r, --root=FILE        make FILE a symlink to the result, and register it
269                          as a garbage collector root"))
270   (display (_ "
271       --log-file         return the log file names for the given derivations"))
272   (newline)
273   (show-build-options-help)
274   (newline)
275   (display (_ "
276   -h, --help             display this help and exit"))
277   (display (_ "
278   -V, --version          display version information and exit"))
279   (newline)
280   (show-bug-report-information))
282 (define %options
283   ;; Specifications of the command-line options.
284   (cons* (option '(#\h "help") #f #f
285                  (lambda args
286                    (show-help)
287                    (exit 0)))
288          (option '(#\V "version") #f #f
289                  (lambda args
290                    (show-version-and-exit "guix build")))
291          (option '(#\S "source") #f #f
292                  (lambda (opt name arg result)
293                    (alist-cons 'source #t result)))
294          (option '("sources") #f #t
295                  (lambda (opt name arg result)
296                    (match arg
297                      ("package"
298                       (alist-cons 'source #t result))
299                      ((or "all" #f)
300                       (alist-cons 'source package-direct-sources result))
301                      ("transitive"
302                       (alist-cons 'source package-transitive-sources result))
303                      (else
304                       (leave (_ "invalid argument: '~a' option argument: ~a, ~
305 must be one of 'package', 'all', or 'transitive'~%")
306                              name arg)))))
307          (option '(#\s "system") #t #f
308                  (lambda (opt name arg result)
309                    (alist-cons 'system arg
310                                (alist-delete 'system result eq?))))
311          (option '("target") #t #f
312                  (lambda (opt name arg result)
313                    (alist-cons 'target arg
314                                (alist-delete 'target result eq?))))
315          (option '(#\d "derivations") #f #f
316                  (lambda (opt name arg result)
317                    (alist-cons 'derivations-only? #t result)))
318          (option '(#\e "expression") #t #f
319                  (lambda (opt name arg result)
320                    (alist-cons 'expression arg result)))
321          (option '(#\n "dry-run") #f #f
322                  (lambda (opt name arg result)
323                    (alist-cons 'dry-run? #t result)))
324          (option '(#\r "root") #t #f
325                  (lambda (opt name arg result)
326                    (alist-cons 'gc-root arg result)))
327          (option '("log-file") #f #f
328                  (lambda (opt name arg result)
329                    (alist-cons 'log-file? #t result)))
330          (option '("with-source") #t #f
331                  (lambda (opt name arg result)
332                    (alist-cons 'with-source arg result)))
333          (option '("no-grafts") #f #f
334                  (lambda (opt name arg result)
335                    (alist-cons 'graft? #f
336                                (alist-delete 'graft? result eq?))))
338          %standard-build-options))
340 (define (options->derivations store opts)
341   "Given OPTS, the result of 'args-fold', return a list of derivations to
342 build."
343   (define package->derivation
344     (match (assoc-ref opts 'target)
345       (#f package-derivation)
346       (triplet
347        (cut package-cross-derivation <> <> triplet <>))))
349   (define src    (assoc-ref opts 'source))
350   (define sys    (assoc-ref opts 'system))
351   (define graft? (assoc-ref opts 'graft?))
353   (parameterize ((%graft? graft?))
354     (let ((opts (options/with-source store
355                                      (options/resolve-packages store opts))))
356       (concatenate
357        (filter-map (match-lambda
358                     (('argument . (? package? p))
359                      (match src
360                        (#f
361                         (list (package->derivation store p sys)))
362                        (#t
363                         (let ((s (package-source p)))
364                           (list (package-source-derivation store s))))
365                        (proc
366                         (map (cut package-source-derivation store <>)
367                              (proc p)))))
368                     (('argument . (? derivation? drv))
369                      (list drv))
370                     (('argument . (? derivation-path? drv))
371                      (list (call-with-input-file drv read-derivation)))
372                     (('argument . (? store-path?))
373                      ;; Nothing to do; maybe for --log-file.
374                      #f)
375                     (_ #f))
376                    opts)))))
378 (define (options/resolve-packages store opts)
379   "Return OPTS with package specification strings replaced by actual
380 packages."
381   (define system
382     (or (assoc-ref opts 'system) (%current-system)))
384   (map (match-lambda
385         (('argument . (? string? spec))
386          (if (store-path? spec)
387              `(argument . ,spec)
388              `(argument . ,(specification->package spec))))
389         (('expression . str)
390          (match (read/eval str)
391            ((? package? p)
392             `(argument . ,p))
393            ((? procedure? proc)
394             (let ((drv (run-with-store store
395                          (mbegin %store-monad
396                            (set-guile-for-build (default-guile))
397                            (proc))
398                          #:system system)))
399               `(argument . ,drv)))
400            ((? gexp? gexp)
401             (let ((drv (run-with-store store
402                          (mbegin %store-monad
403                            (set-guile-for-build (default-guile))
404                            (gexp->derivation "gexp" gexp
405                                              #:system system)))))
406               `(argument . ,drv)))))
407         (opt opt))
408        opts))
410 (define (options/with-source store opts)
411   "Process with 'with-source' options in OPTS, replacing the relevant package
412 arguments with packages that use the specified source."
413   (define new-sources
414     (filter-map (match-lambda
415                  (('with-source . uri)
416                   (cons (package-name->name+version (basename uri))
417                         uri))
418                  (_ #f))
419                 opts))
421   (let loop ((opts    opts)
422              (sources new-sources)
423              (result  '()))
424     (match opts
425       (()
426        (unless (null? sources)
427          (warning (_ "sources do not match any package:~{ ~a~}~%")
428                   (match sources
429                     (((name . uri) ...)
430                      uri))))
431        (reverse result))
432       ((('argument . (? package? p)) tail ...)
433        (let ((source (assoc-ref sources (package-name p))))
434          (loop tail
435                (alist-delete (package-name p) sources)
436                (alist-cons 'argument
437                            (if source
438                                (package-with-source store p source)
439                                p)
440                            result))))
441       ((('with-source . _) tail ...)
442        (loop tail sources result))
443       ((head tail ...)
444        (loop tail sources (cons head result))))))
448 ;;; Entry point.
451 (define (guix-build . args)
452   (with-error-handling
453     ;; Ask for absolute file names so that .drv file names passed from the
454     ;; user to 'read-derivation' are absolute when it returns.
455     (with-fluids ((%file-port-name-canonicalization 'absolute))
456       (let* ((opts  (parse-command-line args %options
457                                         (list %default-options)))
458              (store (open-connection))
459              (drv   (options->derivations store opts))
460              (roots (filter-map (match-lambda
461                                  (('gc-root . root) root)
462                                  (_ #f))
463                                 opts)))
465         (set-build-options-from-command-line store opts)
466         (unless (assoc-ref opts 'log-file?)
467           (show-what-to-build store drv
468                               #:use-substitutes? (assoc-ref opts 'substitutes?)
469                               #:dry-run? (assoc-ref opts 'dry-run?)))
471         (cond ((assoc-ref opts 'log-file?)
472                (for-each (lambda (file)
473                            (let ((log (log-file store file)))
474                              (if log
475                                  (format #t "~a~%" log)
476                                  (leave (_ "no build log for '~a'~%")
477                                         file))))
478                          (delete-duplicates
479                           (append (map derivation-file-name drv)
480                                   (filter-map (match-lambda
481                                                (('argument
482                                                  . (? store-path? file))
483                                                 file)
484                                                (_ #f))
485                                               opts)))))
486               ((assoc-ref opts 'derivations-only?)
487                (format #t "~{~a~%~}" (map derivation-file-name drv))
488                (for-each (cut register-root store <> <>)
489                          (map (compose list derivation-file-name) drv)
490                          roots))
491               ((not (assoc-ref opts 'dry-run?))
492                (and (build-derivations store drv)
493                     (for-each (lambda (d)
494                                 (format #t "~{~a~%~}"
495                                         (map (match-lambda
496                                               ((out-name . out)
497                                                (derivation->output-path
498                                                 d out-name)))
499                                              (derivation-outputs d))))
500                               drv)
501                     (for-each (cut register-root store <> <>)
502                               (map (lambda (drv)
503                                      (map cdr
504                                           (derivation->output-paths drv)))
505                                    drv)
506                               roots))))))))