ci.scm 22 KB

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