ci.scm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2017 Jan Nieuwenhuizen <janneke@gnu.org>
  4. ;;; Copyright © 2018, 2019 Clément Lassieur <clement@lassieur.org>
  5. ;;;
  6. ;;; This file is part of GNU Guix.
  7. ;;;
  8. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  9. ;;; under the terms of the GNU General Public License as published by
  10. ;;; the Free Software Foundation; either version 3 of the License, or (at
  11. ;;; your option) any later version.
  12. ;;;
  13. ;;; GNU Guix is distributed in the hope that it will be useful, but
  14. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;;; GNU General Public License for more details.
  17. ;;;
  18. ;;; You should have received a copy of the GNU General Public License
  19. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  20. (define-module (gnu ci)
  21. #:use-module (guix config)
  22. #:use-module (guix store)
  23. #:use-module (guix grafts)
  24. #:use-module (guix profiles)
  25. #:use-module (guix packages)
  26. #:use-module (guix channels)
  27. #:use-module (guix derivations)
  28. #:use-module (guix build-system)
  29. #:use-module (guix monads)
  30. #:use-module (guix ui)
  31. #:use-module ((guix licenses)
  32. #:select (gpl3+ license? license-name))
  33. #:use-module ((guix utils) #:select (%current-system))
  34. #:use-module ((guix scripts system) #:select (read-operating-system))
  35. #:use-module ((guix scripts pack)
  36. #:select (lookup-compressor self-contained-tarball))
  37. #:use-module (gnu bootloader)
  38. #:use-module (gnu bootloader u-boot)
  39. #:use-module (gnu packages)
  40. #:use-module (gnu packages gcc)
  41. #:use-module (gnu packages base)
  42. #:use-module (gnu packages gawk)
  43. #:use-module (gnu packages guile)
  44. #:use-module (gnu packages gettext)
  45. #:use-module (gnu packages compression)
  46. #:use-module (gnu packages multiprecision)
  47. #:use-module (gnu packages make-bootstrap)
  48. #:use-module (gnu packages package-management)
  49. #:use-module (gnu system)
  50. #:use-module (gnu system vm)
  51. #:use-module (gnu system install)
  52. #:use-module (gnu tests)
  53. #:use-module (srfi srfi-1)
  54. #:use-module (srfi srfi-26)
  55. #:use-module (ice-9 match)
  56. #:export (channel-instance->package
  57. hydra-jobs))
  58. ;;; Commentary:
  59. ;;;
  60. ;;; This file defines build jobs for the Hydra and Cuirass continuation
  61. ;;; integration tools.
  62. ;;;
  63. ;;; Code:
  64. (define* (package->alist store package system
  65. #:optional (package-derivation package-derivation))
  66. "Convert PACKAGE to an alist suitable for Hydra."
  67. (parameterize ((%graft? #f))
  68. `((derivation . ,(derivation-file-name
  69. (package-derivation store package system
  70. #:graft? #f)))
  71. (description . ,(package-synopsis package))
  72. (long-description . ,(package-description package))
  73. ;; XXX: Hydra ignores licenses that are not a <license> structure or a
  74. ;; list thereof.
  75. (license . ,(let loop ((license (package-license package)))
  76. (match license
  77. ((? license?)
  78. (license-name license))
  79. ((lst ...)
  80. (map loop license)))))
  81. (home-page . ,(package-home-page package))
  82. (maintainers . ("bug-guix@gnu.org"))
  83. (max-silent-time . ,(or (assoc-ref (package-properties package)
  84. 'max-silent-time)
  85. 3600)) ;1 hour by default
  86. (timeout . ,(or (assoc-ref (package-properties package) 'timeout)
  87. 72000))))) ;20 hours by default
  88. (define (package-job store job-name package system)
  89. "Return a job called JOB-NAME that builds PACKAGE on SYSTEM."
  90. (let ((job-name (symbol-append job-name (string->symbol ".")
  91. (string->symbol system))))
  92. `(,job-name . ,(cut package->alist store package system))))
  93. (define (package-cross-job store job-name package target system)
  94. "Return a job called TARGET.JOB-NAME that cross-builds PACKAGE for TARGET on
  95. SYSTEM."
  96. `(,(symbol-append (string->symbol target) (string->symbol ".") job-name
  97. (string->symbol ".") (string->symbol system)) .
  98. ,(cute package->alist store package system
  99. (lambda* (store package system #:key graft?)
  100. (package-cross-derivation store package target system
  101. #:graft? graft?)))))
  102. (define %core-packages
  103. ;; Note: Don't put the '-final' package variants because (1) that's
  104. ;; implicit, and (2) they cannot be cross-built (due to the explicit input
  105. ;; chain.)
  106. (list gcc-4.8 gcc-4.9 gcc-5 glibc binutils
  107. gmp mpfr mpc coreutils findutils diffutils patch sed grep
  108. gawk gnu-gettext hello guile-2.0 guile-2.2 zlib gzip xz
  109. %bootstrap-binaries-tarball
  110. %binutils-bootstrap-tarball
  111. (%glibc-bootstrap-tarball)
  112. %gcc-bootstrap-tarball
  113. %guile-bootstrap-tarball
  114. %bootstrap-tarballs))
  115. (define %packages-to-cross-build
  116. %core-packages)
  117. (define %cross-targets
  118. '("mips64el-linux-gnu"
  119. "mips64el-linux-gnuabi64"
  120. "arm-linux-gnueabihf"
  121. "aarch64-linux-gnu"
  122. "powerpc-linux-gnu"
  123. "i586-pc-gnu" ;aka. GNU/Hurd
  124. "i686-w64-mingw32"
  125. "x86_64-w64-mingw32"))
  126. (define %guixsd-supported-systems
  127. '("x86_64-linux" "i686-linux" "armhf-linux"))
  128. (define %u-boot-systems
  129. '("armhf-linux"))
  130. (define (qemu-jobs store system)
  131. "Return a list of jobs that build QEMU images for SYSTEM."
  132. (define (->alist drv)
  133. `((derivation . ,(derivation-file-name drv))
  134. (description . "Stand-alone QEMU image of the GNU system")
  135. (long-description . "This is a demo stand-alone QEMU image of the GNU
  136. system.")
  137. (license . ,(license-name gpl3+))
  138. (max-silent-time . 600)
  139. (timeout . 3600)
  140. (home-page . ,%guix-home-page-url)
  141. (maintainers . ("bug-guix@gnu.org"))))
  142. (define (->job name drv)
  143. (let ((name (symbol-append name (string->symbol ".")
  144. (string->symbol system))))
  145. `(,name . ,(lambda ()
  146. (parameterize ((%graft? #f))
  147. (->alist drv))))))
  148. (define MiB
  149. (expt 2 20))
  150. (if (member system %guixsd-supported-systems)
  151. (if (member system %u-boot-systems)
  152. (list (->job 'flash-image
  153. (run-with-store store
  154. (mbegin %store-monad
  155. (set-guile-for-build (default-guile))
  156. (system-disk-image
  157. (operating-system (inherit installation-os)
  158. (bootloader (bootloader-configuration
  159. (bootloader u-boot-bootloader)
  160. (target #f))))
  161. #:disk-image-size
  162. (* 1500 MiB))))))
  163. (list (->job 'usb-image
  164. (run-with-store store
  165. (mbegin %store-monad
  166. (set-guile-for-build (default-guile))
  167. (system-disk-image installation-os
  168. #:disk-image-size
  169. (* 1500 MiB)))))
  170. (->job 'iso9660-image
  171. (run-with-store store
  172. (mbegin %store-monad
  173. (set-guile-for-build (default-guile))
  174. (system-disk-image installation-os
  175. #:file-system-type
  176. "iso9660"))))))
  177. '()))
  178. (define channel-build-system
  179. ;; Build system used to "convert" a channel instance to a package.
  180. (let* ((build (lambda* (store name inputs
  181. #:key instance system
  182. #:allow-other-keys)
  183. (run-with-store store
  184. (channel-instances->derivation (list instance))
  185. #:system system)))
  186. (lower (lambda* (name #:key system instance #:allow-other-keys)
  187. (bag
  188. (name name)
  189. (system system)
  190. (build build)
  191. (arguments `(#:instance ,instance))))))
  192. (build-system (name 'channel)
  193. (description "Turn a channel instance into a package.")
  194. (lower lower))))
  195. (define (channel-instance->package instance)
  196. "Return a package for the given channel INSTANCE."
  197. (package
  198. (inherit guix)
  199. (version (or (string-take (channel-instance-commit instance) 7)
  200. (string-append (package-version guix) "+")))
  201. (build-system channel-build-system)
  202. (arguments `(#:instance ,instance))
  203. (inputs '())
  204. (native-inputs '())
  205. (propagated-inputs '())))
  206. (define* (system-test-jobs store system
  207. #:key source commit)
  208. "Return a list of jobs for the system tests."
  209. (define instance
  210. (checkout->channel-instance source #:commit commit))
  211. (define (test->thunk test)
  212. (lambda ()
  213. (define drv
  214. (run-with-store store
  215. (mbegin %store-monad
  216. (set-current-system system)
  217. (set-grafting #f)
  218. (set-guile-for-build (default-guile))
  219. (system-test-value test))))
  220. `((derivation . ,(derivation-file-name drv))
  221. (description . ,(format #f "Guix '~a' system test"
  222. (system-test-name test)))
  223. (long-description . ,(system-test-description test))
  224. (license . ,(license-name gpl3+))
  225. (max-silent-time . 600)
  226. (timeout . 3600)
  227. (home-page . ,%guix-home-page-url)
  228. (maintainers . ("bug-guix@gnu.org")))))
  229. (define (->job test)
  230. (let ((name (string->symbol
  231. (string-append "test." (system-test-name test)
  232. "." system))))
  233. (cons name (test->thunk test))))
  234. (if (and (member system %guixsd-supported-systems)
  235. ;; XXX: Our build farm has too few ARMv7 machines and they are very
  236. ;; slow, so skip system tests there.
  237. (not (string=? system "armhf-linux")))
  238. ;; Override the value of 'current-guix' used by system tests. Using a
  239. ;; channel instance makes tests that rely on 'current-guix' less
  240. ;; expensive. It also makes sure we get a valid Guix package when this
  241. ;; code is not running from a checkout.
  242. (parameterize ((current-guix-package
  243. (channel-instance->package instance)))
  244. (map ->job (all-system-tests)))
  245. '()))
  246. (define (tarball-jobs store system)
  247. "Return Hydra jobs to build the self-contained Guix binary tarball."
  248. (define (->alist drv)
  249. `((derivation . ,(derivation-file-name drv))
  250. (description . "Stand-alone binary Guix tarball")
  251. (long-description . "This is a tarball containing binaries of Guix and
  252. all its dependencies, and ready to be installed on \"foreign\" distributions.")
  253. (license . ,(license-name gpl3+))
  254. (home-page . ,%guix-home-page-url)
  255. (maintainers . ("bug-guix@gnu.org"))))
  256. (define (->job name drv)
  257. (let ((name (symbol-append name (string->symbol ".")
  258. (string->symbol system))))
  259. `(,name . ,(lambda ()
  260. (parameterize ((%graft? #f))
  261. (->alist drv))))))
  262. ;; XXX: Add a job for the stable Guix?
  263. (list (->job 'binary-tarball
  264. (run-with-store store
  265. (mbegin %store-monad
  266. (set-guile-for-build (default-guile))
  267. (>>= (profile-derivation (packages->manifest (list guix)))
  268. (lambda (profile)
  269. (self-contained-tarball "guix-binary" profile
  270. #:localstatedir? #t
  271. #:compressor
  272. (lookup-compressor "xz")))))
  273. #:system system))))
  274. (define job-name
  275. ;; Return the name of a package's job.
  276. (compose string->symbol
  277. (cut package-full-name <> "-")))
  278. (define package->job
  279. (let ((base-packages
  280. (delete-duplicates
  281. (append-map (match-lambda
  282. ((_ package _ ...)
  283. (match (package-transitive-inputs package)
  284. (((_ inputs _ ...) ...)
  285. inputs))))
  286. (%final-inputs)))))
  287. (lambda (store package system)
  288. "Return a job for PACKAGE on SYSTEM, or #f if this combination is not
  289. valid."
  290. (cond ((member package base-packages)
  291. (package-job store (symbol-append 'base. (job-name package))
  292. package system))
  293. ((supported-package? package system)
  294. (let ((drv (package-derivation store package system
  295. #:graft? #f)))
  296. (and (substitutable-derivation? drv)
  297. (package-job store (job-name package)
  298. package system))))
  299. (else
  300. #f)))))
  301. (define (all-packages)
  302. "Return the list of packages to build."
  303. (define (adjust package result)
  304. (cond ((package-replacement package)
  305. ;; XXX: If PACKAGE and its replacement have the same name/version,
  306. ;; then both Cuirass jobs will have the same name, which
  307. ;; effectively means that the second one will be ignored. Thus,
  308. ;; return the replacement first.
  309. (cons* (package-replacement package) ;build both
  310. package
  311. result))
  312. ((package-superseded package)
  313. result) ;don't build it
  314. (else
  315. (cons package result))))
  316. (fold-packages adjust
  317. (fold adjust '() ;include base packages
  318. (match (%final-inputs)
  319. (((labels packages _ ...) ...)
  320. packages)))
  321. #:select? (const #t))) ;include hidden packages
  322. (define (arguments->manifests arguments)
  323. "Return the list of manifests extracted from ARGUMENTS."
  324. (map (match-lambda
  325. ((input-name . relative-path)
  326. (let* ((checkout (assq-ref arguments (string->symbol input-name)))
  327. (base (assq-ref checkout 'file-name)))
  328. (in-vicinity base relative-path))))
  329. (assq-ref arguments 'manifests)))
  330. (define (manifests->packages store manifests)
  331. "Return the list of packages found in MANIFESTS."
  332. (define (load-manifest manifest)
  333. (save-module-excursion
  334. (lambda ()
  335. (set-current-module (make-user-module '((guix profiles) (gnu))))
  336. (primitive-load manifest))))
  337. (delete-duplicates!
  338. (map manifest-entry-item
  339. (append-map (compose manifest-entries
  340. load-manifest)
  341. manifests))))
  342. (define (find-current-checkout arguments)
  343. "Find the first checkout of ARGUMENTS that provided the current file.
  344. Return #f if no such checkout is found."
  345. (let ((current-root
  346. (canonicalize-path
  347. (string-append (dirname (current-filename)) "/.."))))
  348. (find (lambda (argument)
  349. (and=> (assq-ref argument 'file-name)
  350. (lambda (name)
  351. (string=? name current-root)))) arguments)))
  352. ;;;
  353. ;;; Hydra entry point.
  354. ;;;
  355. (define (hydra-jobs store arguments)
  356. "Return Hydra jobs."
  357. (define subset
  358. (match (assoc-ref arguments 'subset)
  359. ("core" 'core) ; only build core packages
  360. ("hello" 'hello) ; only build hello
  361. (((? string?) (? string?) ...) 'list) ; only build selected list of packages
  362. ("manifests" 'manifests) ; only build packages in the list of manifests
  363. (_ 'all))) ; build everything
  364. (define systems
  365. (match (assoc-ref arguments 'systems)
  366. (#f %hydra-supported-systems)
  367. ((lst ...) lst)
  368. ((? string? str) (call-with-input-string str read))))
  369. (define checkout
  370. (or (find-current-checkout arguments)
  371. (assq-ref arguments 'superior-guix-checkout)))
  372. (define commit
  373. (assq-ref checkout 'revision))
  374. (define source
  375. (assq-ref checkout 'file-name))
  376. (define (cross-jobs system)
  377. (define (from-32-to-64? target)
  378. ;; Return true if SYSTEM is 32-bit and TARGET is 64-bit. This hack
  379. ;; prevents known-to-fail cross-builds from i686-linux or armhf-linux to
  380. ;; mips64el-linux-gnuabi64.
  381. (and (or (string-prefix? "i686-" system)
  382. (string-prefix? "i586-" system)
  383. (string-prefix? "armhf-" system))
  384. (string-contains target "64"))) ;x86_64, mips64el, aarch64, etc.
  385. (define (same? target)
  386. ;; Return true if SYSTEM and TARGET are the same thing. This is so we
  387. ;; don't try to cross-compile to 'mips64el-linux-gnu' from
  388. ;; 'mips64el-linux'.
  389. (or (string-contains target system)
  390. (and (string-prefix? "armhf" system) ;armhf-linux
  391. (string-prefix? "arm" target)))) ;arm-linux-gnueabihf
  392. (define (pointless? target)
  393. ;; Return #t if it makes no sense to cross-build to TARGET from SYSTEM.
  394. (match system
  395. ((or "x86_64-linux" "i686-linux")
  396. (if (string-contains target "mingw")
  397. (not (string=? "x86_64-linux" system))
  398. #f))
  399. (_
  400. ;; Don't try to cross-compile from non-Intel platforms: this isn't
  401. ;; very useful and these are often brittle configurations.
  402. #t)))
  403. (define (either proc1 proc2 proc3)
  404. (lambda (x)
  405. (or (proc1 x) (proc2 x) (proc3 x))))
  406. (append-map (lambda (target)
  407. (map (lambda (package)
  408. (package-cross-job store (job-name package)
  409. package target system))
  410. %packages-to-cross-build))
  411. (remove (either from-32-to-64? same? pointless?)
  412. %cross-targets)))
  413. ;; Turn off grafts. Grafting is meant to happen on the user's machines.
  414. (parameterize ((%graft? #f))
  415. ;; Return one job for each package, except bootstrap packages.
  416. (append-map (lambda (system)
  417. (format (current-error-port)
  418. "evaluating for '~a' (heap size: ~a MiB)...~%"
  419. system
  420. (round
  421. (/ (assoc-ref (gc-stats) 'heap-size)
  422. (expt 2. 20))))
  423. (invalidate-derivation-caches!)
  424. (case subset
  425. ((all)
  426. ;; Build everything, including replacements.
  427. (let ((all (all-packages))
  428. (job (lambda (package)
  429. (package->job store package
  430. system))))
  431. (append (filter-map job all)
  432. (qemu-jobs store system)
  433. (system-test-jobs store system
  434. #:source source
  435. #:commit commit)
  436. (tarball-jobs store system)
  437. (cross-jobs system))))
  438. ((core)
  439. ;; Build core packages only.
  440. (append (map (lambda (package)
  441. (package-job store (job-name package)
  442. package system))
  443. %core-packages)
  444. (cross-jobs system)))
  445. ((hello)
  446. ;; Build hello package only.
  447. (if (string=? system (%current-system))
  448. (let ((hello (specification->package "hello")))
  449. (list (package-job store (job-name hello) hello system)))
  450. '()))
  451. ((list)
  452. ;; Build selected list of packages only.
  453. (if (string=? system (%current-system))
  454. (let* ((names (assoc-ref arguments 'subset))
  455. (packages (map specification->package names)))
  456. (map (lambda (package)
  457. (package-job store (job-name package)
  458. package system))
  459. packages))
  460. '()))
  461. ((manifests)
  462. ;; Build packages in the list of manifests.
  463. (let* ((manifests (arguments->manifests arguments))
  464. (packages (manifests->packages store manifests)))
  465. (map (lambda (package)
  466. (package-job store (job-name package)
  467. package system))
  468. packages)))
  469. (else
  470. (error "unknown subset" subset))))
  471. systems)))