ci.scm 20 KB

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