gnu: gss: Update to 1.0.4.
[jackhill/guix/guix.git] / gnu / ci.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2012-2021 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2017, 2020 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
4 ;;; Copyright © 2018, 2019 Clément Lassieur <clement@lassieur.org>
5 ;;; Copyright © 2020 Julien Lepiller <julien@lepiller.eu>
6 ;;; Copyright © 2020, 2021 Mathieu Othacehe <othacehe@gnu.org>
7 ;;;
8 ;;; This file is part of GNU Guix.
9 ;;;
10 ;;; GNU Guix is free software; you can redistribute it and/or modify it
11 ;;; under the terms of the GNU General Public License as published by
12 ;;; the Free Software Foundation; either version 3 of the License, or (at
13 ;;; your option) any later version.
14 ;;;
15 ;;; GNU Guix is distributed in the hope that it will be useful, but
16 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;;; GNU General Public License for more details.
19 ;;;
20 ;;; You should have received a copy of the GNU General Public License
21 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
22
23 (define-module (gnu ci)
24 #:use-module (guix channels)
25 #:use-module (guix config)
26 #:use-module (guix describe)
27 #:use-module (guix store)
28 #:use-module (guix grafts)
29 #:use-module (guix profiles)
30 #:use-module (guix packages)
31 #:autoload (guix transformations) (tunable-package? tuned-package)
32 #:use-module (guix channels)
33 #:use-module (guix config)
34 #:use-module (guix derivations)
35 #:use-module (guix build-system)
36 #:use-module (guix monads)
37 #:use-module (guix gexp)
38 #:use-module (guix ui)
39 #:use-module ((guix licenses)
40 #:select (gpl3+ license? license-name))
41 #:use-module ((guix utils) #:select (%current-system))
42 #:use-module ((guix scripts pack)
43 #:select (lookup-compressor self-contained-tarball))
44 #:use-module (gnu bootloader)
45 #:use-module (gnu bootloader u-boot)
46 #:use-module (gnu image)
47 #:use-module (gnu packages)
48 #:use-module (gnu packages gcc)
49 #:use-module (gnu packages base)
50 #:use-module (gnu packages gawk)
51 #:use-module (gnu packages guile)
52 #:use-module (gnu packages gettext)
53 #:use-module (gnu packages compression)
54 #:use-module (gnu packages multiprecision)
55 #:use-module (gnu packages make-bootstrap)
56 #:use-module (gnu packages package-management)
57 #:use-module (guix platform)
58 #:use-module (gnu system)
59 #:use-module (gnu system image)
60 #:use-module (gnu system vm)
61 #:use-module (gnu system install)
62 #:use-module (gnu system images hurd)
63 #:use-module (gnu system images novena)
64 #:use-module (gnu system images pine64)
65 #:use-module (gnu system images pinebook-pro)
66 #:use-module (gnu tests)
67 #:use-module (srfi srfi-1)
68 #:use-module (srfi srfi-26)
69 #:use-module (ice-9 match)
70 #:export (derivation->job
71 image->job
72
73 %core-packages
74 channel-source->package
75
76 arguments->systems
77 cuirass-jobs))
78
79 ;;; Commentary:
80 ;;;
81 ;;; This file defines build jobs for Cuirass.
82 ;;;
83 ;;; Code:
84
85 (define* (derivation->job name drv
86 #:key
87 (max-silent-time 3600)
88 (timeout (* 5 3600)))
89 "Return a Cuirass job called NAME and describing DRV.
90
91 MAX-SILENT-TIME and TIMEOUT are build options passed to the daemon when
92 building the derivation."
93 `((#:job-name . ,name)
94 (#:derivation . ,(derivation-file-name drv))
95 (#:inputs . ,(map (compose derivation-file-name
96 derivation-input-derivation)
97 (derivation-inputs drv)))
98 (#:outputs . ,(filter-map
99 (lambda (res)
100 (match res
101 ((name . path)
102 `(,name . ,path))))
103 (derivation->output-paths drv)))
104 (#:nix-name . ,(derivation-name drv))
105 (#:system . ,(derivation-system drv))
106 (#:max-silent-time . ,max-silent-time)
107 (#:timeout . ,timeout)))
108
109 (define* (package-job store job-name package system
110 #:key cross? target (suffix ""))
111 "Return a job called JOB-NAME that builds PACKAGE on SYSTEM."
112 (let ((job-name (string-append job-name "." system suffix)))
113 (parameterize ((%graft? #f))
114 (let* ((drv (if cross?
115 (package-cross-derivation store package target system
116 #:graft? #f)
117 (package-derivation store package system
118 #:graft? #f)))
119 (max-silent-time (or (assoc-ref (package-properties package)
120 'max-silent-time)
121 3600))
122 (timeout (or (assoc-ref (package-properties package)
123 'timeout)
124 72000)))
125 (derivation->job job-name drv
126 #:max-silent-time max-silent-time
127 #:timeout timeout)))))
128
129 (define (package-cross-job store job-name package target system)
130 "Return a job called TARGET.JOB-NAME that cross-builds PACKAGE for TARGET on
131 SYSTEM."
132 (let ((name (string-append target "." job-name)))
133 (package-job store name package system
134 #:cross? #t
135 #:target target)))
136
137 (define %core-packages
138 ;; Note: Don't put the '-final' package variants because (1) that's
139 ;; implicit, and (2) they cannot be cross-built (due to the explicit input
140 ;; chain.)
141 (list gcc-8 gcc-9 gcc-10 gcc-11 glibc binutils
142 gmp mpfr mpc coreutils findutils diffutils patch sed grep
143 gawk gnu-gettext hello guile-2.2 guile-3.0 zlib gzip xz guix
144 %bootstrap-binaries-tarball
145 %binutils-bootstrap-tarball
146 (%glibc-bootstrap-tarball)
147 %gcc-bootstrap-tarball
148 %guile-bootstrap-tarball
149 %bootstrap-tarballs))
150
151 (define (commencement-packages system)
152 "Return the list of bootstrap packages from the commencement module for
153 SYSTEM."
154 ;; Only include packages supported on SYSTEM. For example, the Mes
155 ;; bootstrap graph is currently not supported on ARM so it should be
156 ;; excluded.
157 (filter (lambda (obj)
158 (and (package? obj)
159 (supported-package? obj system)))
160 (module-map (lambda (sym var)
161 (variable-ref var))
162 (resolve-module '(gnu packages commencement)))))
163
164 (define (packages-to-cross-build target)
165 "Return the list of packages to cross-build for TARGET."
166 ;; Don't cross-build the bootstrap tarballs for MinGW.
167 (if (string-contains target "mingw")
168 (drop-right %core-packages 6)
169 %core-packages))
170
171 (define (cross-jobs store system)
172 "Return a list of cross-compilation jobs for SYSTEM."
173 (define (from-32-to-64? target)
174 ;; Return true if SYSTEM is 32-bit and TARGET is 64-bit. This hack
175 ;; prevents known-to-fail cross-builds from i686-linux or armhf-linux to
176 ;; mips64el-linux-gnuabi64.
177 (and (or (string-prefix? "i686-" system)
178 (string-prefix? "i586-" system)
179 (string-prefix? "armhf-" system))
180 (string-contains target "64"))) ;x86_64, mips64el, aarch64, etc.
181
182 (define (same? target)
183 ;; Return true if SYSTEM and TARGET are the same thing. This is so we
184 ;; don't try to cross-compile to 'mips64el-linux-gnu' from
185 ;; 'mips64el-linux'.
186 (or (string-contains target system)
187 (and (string-prefix? "armhf" system) ;armhf-linux
188 (string-prefix? "arm" target)))) ;arm-linux-gnueabihf
189
190 (define (pointless? target)
191 ;; Return #t if it makes no sense to cross-build to TARGET from SYSTEM.
192 (match system
193 ((or "x86_64-linux" "i686-linux")
194 (if (string-contains target "mingw")
195 (not (string=? "x86_64-linux" system))
196 #f))
197 (_
198 ;; Don't try to cross-compile from non-Intel platforms: this isn't
199 ;; very useful and these are often brittle configurations.
200 #t)))
201
202 (define (either proc1 proc2 proc3)
203 (lambda (x)
204 (or (proc1 x) (proc2 x) (proc3 x))))
205
206 (append-map (lambda (target)
207 (map (lambda (package)
208 (package-cross-job store (job-name package)
209 package target system))
210 (packages-to-cross-build target)))
211 (remove (either from-32-to-64? same? pointless?)
212 (targets))))
213
214 (define* (guix-jobs store systems #:key source commit)
215 "Return a list of jobs for Guix itself."
216 (define build
217 (primitive-load (string-append source "/build-aux/build-self.scm")))
218
219 (map
220 (lambda (system)
221 (let ((name (string->symbol
222 (string-append "guix." system)))
223 (drv (run-with-store store
224 (build source #:version commit #:system system
225 #:pull-version 1
226 #:guile-version "2.2"))))
227 (derivation->job name drv)))
228 systems))
229
230 ;; Architectures that are able to build or cross-build Guix System images.
231 ;; This does not mean that other architectures are not supported, only that
232 ;; they are often not fast enough to support Guix System images building.
233 (define %guix-system-supported-systems
234 '("x86_64-linux" "i686-linux"))
235
236 (define %guix-system-images
237 (list hurd-barebones-qcow2-image
238 pine64-barebones-raw-image
239 pinebook-pro-barebones-raw-image
240 novena-barebones-raw-image))
241
242 (define (hours hours)
243 (* 3600 hours))
244
245 (define* (image->job store image
246 #:key name system)
247 "Return the job for IMAGE on SYSTEM. If NAME is passed, use it as job name,
248 otherwise use the IMAGE name."
249 (let* ((image-name (or name
250 (symbol->string (image-name image))))
251 (name (string-append image-name "." system))
252 (drv (run-with-store store
253 (mbegin %store-monad
254 (set-guile-for-build (default-guile))
255 (lower-object (system-image image))))))
256 (parameterize ((%graft? #f))
257 (derivation->job name drv))))
258
259 (define (image-jobs store system)
260 "Return a list of jobs that build images for SYSTEM."
261 (define MiB
262 (expt 2 20))
263
264 (if (member system %guix-system-supported-systems)
265 `(,(image->job store
266 (image
267 (inherit efi-disk-image)
268 (operating-system installation-os))
269 #:name "usb-image"
270 #:system system)
271 ,(image->job
272 store
273 (image
274 (inherit (image-with-label
275 iso9660-image
276 (string-append "GUIX_" system "_"
277 (if (> (string-length %guix-version) 7)
278 (substring %guix-version 0 7)
279 %guix-version))))
280 (operating-system installation-os))
281 #:name "iso9660-image"
282 #:system system)
283 ;; Only cross-compile Guix System images from x86_64-linux for now.
284 ,@(if (string=? system "x86_64-linux")
285 (map (cut image->job store <>
286 #:system system)
287 %guix-system-images)
288 '()))
289 '()))
290
291 (define channel-build-system
292 ;; Build system used to "convert" a channel instance to a package.
293 (let* ((build (lambda* (name inputs
294 #:key source commit system
295 #:allow-other-keys)
296 (mlet* %store-monad ((source (if (string? source)
297 (return source)
298 (lower-object source)))
299 (instance
300 -> (checkout->channel-instance
301 source #:commit commit)))
302 (channel-instances->derivation (list instance)))))
303 (lower (lambda* (name #:key system source commit
304 #:allow-other-keys)
305 (bag
306 (name name)
307 (system system)
308 (build build)
309 (arguments `(#:source ,source
310 #:commit ,commit))))))
311 (build-system (name 'channel)
312 (description "Turn a channel instance into a package.")
313 (lower lower))))
314
315 (define* (channel-source->package source #:key commit)
316 "Return a package for the given channel SOURCE, a lowerable object."
317 (package
318 (inherit guix)
319 (version (string-append (package-version guix) "+"))
320 (build-system channel-build-system)
321 (arguments `(#:source ,source
322 #:commit ,commit))
323 (inputs '())
324 (native-inputs '())
325 (propagated-inputs '())))
326
327 (define* (system-test-jobs store system
328 #:key source commit)
329 "Return a list of jobs for the system tests."
330 (define (->job test)
331 (let ((name (string-append "test." (system-test-name test)
332 "." system))
333 (drv (run-with-store store
334 (mbegin %store-monad
335 (set-current-system system)
336 (set-grafting #f)
337 (set-guile-for-build (default-guile))
338 (system-test-value test)))))
339
340 (derivation->job name drv)))
341
342 (if (member system %guix-system-supported-systems)
343 ;; Override the value of 'current-guix' used by system tests. Using a
344 ;; channel instance makes tests that rely on 'current-guix' less
345 ;; expensive. It also makes sure we get a valid Guix package when this
346 ;; code is not running from a checkout.
347 (parameterize ((current-guix-package
348 (channel-source->package source #:commit commit)))
349 (map ->job (all-system-tests)))
350 '()))
351
352 (define (tarball-jobs store system)
353 "Return jobs to build the self-contained Guix binary tarball."
354 (define (->job name drv)
355 (let ((name (string-append name "." system)))
356 (parameterize ((%graft? #f))
357 (derivation->job name drv))))
358
359 ;; XXX: Add a job for the stable Guix?
360 (list
361 (->job "binary-tarball"
362 (run-with-store store
363 (mbegin %store-monad
364 (set-guile-for-build (default-guile))
365 (>>= (profile-derivation (packages->manifest (list guix)))
366 (lambda (profile)
367 (self-contained-tarball "guix-binary" profile
368 #:profile-name "current-guix"
369 #:localstatedir? #t
370 #:compressor
371 (lookup-compressor "xz")))))
372 #:system system))))
373
374 (define job-name
375 ;; Return the name of a package's job.
376 package-name)
377
378 (define package->job
379 (let ((base-packages
380 (delete-duplicates
381 (append-map (match-lambda
382 ((_ package _ ...)
383 (match (package-transitive-inputs package)
384 (((_ inputs _ ...) ...)
385 inputs))))
386 (%final-inputs)))))
387 (lambda* (store package system #:key (suffix ""))
388 "Return a job for PACKAGE on SYSTEM, or #f if this combination is not
389 valid. Append SUFFIX to the job name."
390 (cond ((member package base-packages)
391 (package-job store (string-append "base." (job-name package))
392 package system #:suffix suffix))
393 ((supported-package? package system)
394 (let ((drv (package-derivation store package system
395 #:graft? #f)))
396 (and (substitutable-derivation? drv)
397 (package-job store (job-name package)
398 package system #:suffix suffix))))
399 (else
400 #f)))))
401
402 (define %x86-64-micro-architectures
403 ;; Micro-architectures for which we build tuned variants.
404 '("westmere" "ivybridge" "haswell" "skylake" "skylake-avx512"))
405
406 (define (tuned-package-jobs store package system)
407 "Return a list of jobs for PACKAGE tuned for SYSTEM's micro-architectures."
408 (filter-map (lambda (micro-architecture)
409 (define suffix
410 (string-append "." micro-architecture))
411
412 (package->job store
413 (tuned-package package micro-architecture)
414 system
415 #:suffix suffix))
416 (match system
417 ("x86_64-linux" %x86-64-micro-architectures)
418 (_ '()))))
419
420 (define (all-packages)
421 "Return the list of packages to build."
422 (define (adjust package result)
423 (cond ((package-replacement package)
424 ;; XXX: If PACKAGE and its replacement have the same name/version,
425 ;; then both Cuirass jobs will have the same name, which
426 ;; effectively means that the second one will be ignored. Thus,
427 ;; return the replacement first.
428 (cons* (package-replacement package) ;build both
429 package
430 result))
431 ((package-superseded package)
432 result) ;don't build it
433 (else
434 (cons package result))))
435
436 (fold-packages adjust
437 (fold adjust '() ;include base packages
438 (match (%final-inputs)
439 (((labels packages _ ...) ...)
440 packages)))
441 #:select? (const #t))) ;include hidden packages
442
443 (define (arguments->manifests arguments channels)
444 "Return the list of manifests extracted from ARGUMENTS."
445 (map (lambda (manifest)
446 (any (lambda (checkout)
447 (let ((path (in-vicinity checkout manifest)))
448 (and (file-exists? path)
449 path)))
450 (map channel-url channels)))
451 arguments))
452
453 (define (manifests->jobs store manifests)
454 "Return the list of jobs for the entries in MANIFESTS, a list of file
455 names."
456 (define (load-manifest manifest)
457 (save-module-excursion
458 (lambda ()
459 (set-current-module (make-user-module '((guix profiles) (gnu))))
460 (primitive-load manifest))))
461
462 (define (manifest-entry-job-name entry)
463 (string-append (manifest-entry-name entry) "-"
464 (manifest-entry-version entry)))
465
466 (define (manifest-entry->job entry)
467 (let* ((obj (manifest-entry-item entry))
468 (drv (parameterize ((%graft? #f))
469 (run-with-store store
470 (lower-object obj))))
471 (max-silent-time (or (and (package? obj)
472 (assoc-ref (package-properties obj)
473 'max-silent-time))
474 3600))
475 (timeout (or (and (package? obj)
476 (assoc-ref (package-properties obj) 'timeout))
477 (* 5 3600))))
478 (derivation->job (manifest-entry-job-name entry) drv
479 #:max-silent-time max-silent-time
480 #:timeout timeout)))
481
482 (map manifest-entry->job
483 (delete-duplicates
484 (append-map (compose manifest-entries load-manifest)
485 manifests)
486 manifest-entry=?)))
487
488 (define (arguments->systems arguments)
489 "Return the systems list from ARGUMENTS."
490 (match (assoc-ref arguments 'systems)
491 (#f %cuirass-supported-systems)
492 ((lst ...) lst)
493 ((? string? str) (call-with-input-string str read))))
494
495 \f
496 ;;;
497 ;;; Cuirass entry point.
498 ;;;
499
500 (define (cuirass-jobs store arguments)
501 "Register Cuirass jobs."
502 (define subset
503 (assoc-ref arguments 'subset))
504
505 (define systems
506 (arguments->systems arguments))
507
508 (define channels
509 (let ((channels (assq-ref arguments 'channels)))
510 (map sexp->channel channels)))
511
512 (define guix
513 (find guix-channel? channels))
514
515 (define commit
516 (channel-commit guix))
517
518 (define source
519 (channel-url guix))
520
521 ;; Turn off grafts. Grafting is meant to happen on the user's machines.
522 (parameterize ((%graft? #f))
523 ;; Return one job for each package, except bootstrap packages.
524 (append-map
525 (lambda (system)
526 (format (current-error-port)
527 "evaluating for '~a' (heap size: ~a MiB)...~%"
528 system
529 (round
530 (/ (assoc-ref (gc-stats) 'heap-size)
531 (expt 2. 20))))
532 (invalidate-derivation-caches!)
533 (match subset
534 ('all
535 ;; Build everything, including replacements.
536 (let ((all (all-packages))
537 (jobs (lambda (package)
538 (match (package->job store package system)
539 (#f '())
540 (main-job
541 (cons main-job
542 (if (tunable-package? package)
543 (tuned-package-jobs store package system)
544 '())))))))
545 (append
546 (append-map jobs all)
547 (cross-jobs store system))))
548 ('core
549 ;; Build core packages only.
550 (append
551 (map (lambda (package)
552 (package-job store (job-name package)
553 package system))
554 (append (commencement-packages system) %core-packages))
555 (cross-jobs store system)))
556 ('guix
557 ;; Build Guix modules only.
558 (guix-jobs store systems
559 #:source source
560 #:commit commit))
561 ('hello
562 ;; Build hello package only.
563 (let ((hello (specification->package "hello")))
564 (list (package-job store (job-name hello)
565 hello system))))
566 ('images
567 ;; Build Guix System images only.
568 (image-jobs store system))
569 ('system-tests
570 ;; Build Guix System tests only.
571 (system-test-jobs store system
572 #:source source
573 #:commit commit))
574 ('tarball
575 ;; Build Guix tarball only.
576 (tarball-jobs store system))
577 (('custom . modules)
578 ;; Build custom modules jobs only.
579 (append-map
580 (lambda (module)
581 (let ((proc (module-ref
582 (resolve-interface module)
583 'cuirass-jobs)))
584 (proc store arguments)))
585 modules))
586 (('channels . channels)
587 ;; Build only the packages from CHANNELS.
588 (let ((all (all-packages)))
589 (filter-map
590 (lambda (package)
591 (any (lambda (channel)
592 (and (member (channel-name channel) channels)
593 (package->job store package system)))
594 (package-channels package)))
595 all)))
596 (('packages . rest)
597 ;; Build selected list of packages only.
598 (let ((packages (map specification->package rest)))
599 (map (lambda (package)
600 (package-job store (job-name package)
601 package system))
602 packages)))
603 (('manifests . rest)
604 ;; Build packages in the list of manifests.
605 (let ((manifests (arguments->manifests rest channels)))
606 (manifests->jobs store manifests)))
607 (else
608 (error "unknown subset" subset))))
609 systems)))