pack.scm 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2015, 2017, 2018, 2019, 2020, 2021 Ludovic Courtès <ludo@gnu.org>
  3. ;;; Copyright © 2017, 2018 Ricardo Wurmus <rekado@elephly.net>
  4. ;;; Copyright © 2018 Konrad Hinsen <konrad.hinsen@fastmail.net>
  5. ;;; Copyright © 2018 Chris Marusich <cmmarusich@gmail.com>
  6. ;;; Copyright © 2018 Efraim Flashner <efraim@flashner.co.il>
  7. ;;; Copyright © 2020 Tobias Geerinckx-Rice <me@tobias.gr>
  8. ;;; Copyright © 2020 Maxim Cournoyer <maxim.cournoyer@gmail.com>
  9. ;;; Copyright © 2020 Eric Bavier <bavier@posteo.net>
  10. ;;; Copyright © 2021 Maxim Cournoyer <maxim.cournoyer@gmail.com>
  11. ;;;
  12. ;;; This file is part of GNU Guix.
  13. ;;;
  14. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  15. ;;; under the terms of the GNU General Public License as published by
  16. ;;; the Free Software Foundation; either version 3 of the License, or (at
  17. ;;; your option) any later version.
  18. ;;;
  19. ;;; GNU Guix is distributed in the hope that it will be useful, but
  20. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  21. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. ;;; GNU General Public License for more details.
  23. ;;;
  24. ;;; You should have received a copy of the GNU General Public License
  25. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  26. (define-module (guix scripts pack)
  27. #:use-module (guix scripts)
  28. #:use-module (guix ui)
  29. #:use-module (guix gexp)
  30. #:use-module ((guix build utils) #:select (%xz-parallel-args))
  31. #:use-module (guix utils)
  32. #:use-module (guix store)
  33. #:use-module ((guix status) #:select (with-status-verbosity))
  34. #:use-module ((guix self) #:select (make-config.scm))
  35. #:use-module (guix grafts)
  36. #:autoload (guix inferior) (inferior-package?
  37. inferior-package-name
  38. inferior-package-version)
  39. #:use-module (guix monads)
  40. #:use-module (guix modules)
  41. #:use-module (guix packages)
  42. #:use-module (guix profiles)
  43. #:use-module (guix describe)
  44. #:use-module (guix derivations)
  45. #:use-module (guix search-paths)
  46. #:use-module (guix build-system gnu)
  47. #:use-module (guix scripts build)
  48. #:use-module (guix transformations)
  49. #:use-module ((guix self) #:select (make-config.scm))
  50. #:use-module (gnu packages)
  51. #:use-module (gnu packages bootstrap)
  52. #:use-module ((gnu packages compression) #:hide (zip))
  53. #:use-module (gnu packages guile)
  54. #:use-module (gnu packages base)
  55. #:autoload (gnu packages package-management) (guix)
  56. #:autoload (gnu packages gnupg) (guile-gcrypt)
  57. #:autoload (gnu packages guile) (guile2.0-json guile-json)
  58. #:use-module (srfi srfi-1)
  59. #:use-module (srfi srfi-9)
  60. #:use-module (srfi srfi-26)
  61. #:use-module (srfi srfi-37)
  62. #:use-module (ice-9 match)
  63. #:export (compressor?
  64. compressor-name
  65. compressor-extenstion
  66. compressor-command
  67. %compressors
  68. lookup-compressor
  69. self-contained-tarball
  70. debian-archive
  71. docker-image
  72. squashfs-image
  73. %formats
  74. guix-pack))
  75. ;; Type of a compression tool.
  76. (define-record-type <compressor>
  77. (compressor name extension command)
  78. compressor?
  79. (name compressor-name) ;string (e.g., "gzip")
  80. (extension compressor-extension) ;string (e.g., ".lz")
  81. (command compressor-command)) ;gexp (e.g., #~(list "/gnu/store/…/gzip"
  82. ; "-9n" ))
  83. (define %compressors
  84. ;; Available compression tools.
  85. (list (compressor "gzip" ".gz"
  86. #~(list #+(file-append gzip "/bin/gzip") "-9n"))
  87. (compressor "lzip" ".lz"
  88. #~(list #+(file-append lzip "/bin/lzip") "-9"))
  89. (compressor "xz" ".xz"
  90. #~(append (list #+(file-append xz "/bin/xz")
  91. "-e")
  92. (%xz-parallel-args)))
  93. (compressor "bzip2" ".bz2"
  94. #~(list #+(file-append bzip2 "/bin/bzip2") "-9"))
  95. (compressor "zstd" ".zst"
  96. ;; The default level 3 compresses better than gzip in a
  97. ;; fraction of the time, while the highest level 19
  98. ;; (de)compresses more slowly and worse than xz.
  99. #~(list #+(file-append zstd "/bin/zstd") "-3"))
  100. (compressor "none" "" #f)))
  101. ;; This one is only for use in this module, so don't put it in %compressors.
  102. (define bootstrap-xz
  103. (compressor "bootstrap-xz" ".xz"
  104. #~(append (list #+(file-append %bootstrap-coreutils&co "/bin/xz")
  105. "-e")
  106. (%xz-parallel-args))))
  107. (define (lookup-compressor name)
  108. "Return the compressor object called NAME. Error out if it could not be
  109. found."
  110. (or (find (match-lambda
  111. (($ <compressor> name*)
  112. (string=? name* name)))
  113. %compressors)
  114. (leave (G_ "~a: compressor not found~%") name)))
  115. (define not-config?
  116. ;; Select (guix …) and (gnu …) modules, except (guix config).
  117. (match-lambda
  118. (('guix 'config) #f)
  119. (('guix _ ...) #t)
  120. (('gnu _ ...) #t)
  121. (_ #f)))
  122. (define gcrypt-sqlite3&co
  123. ;; Guile-Gcrypt, Guile-SQLite3, and their propagated inputs.
  124. (append-map (lambda (package)
  125. (cons package
  126. (match (package-transitive-propagated-inputs package)
  127. (((labels packages) ...)
  128. packages))))
  129. (list guile-gcrypt guile-sqlite3)))
  130. (define (store-database items)
  131. "Return a directory containing a store database where all of ITEMS and their
  132. dependencies are registered."
  133. (define schema
  134. (local-file (search-path %load-path
  135. "guix/store/schema.sql")))
  136. (define labels
  137. (map (lambda (n)
  138. (string-append "closure" (number->string n)))
  139. (iota (length items))))
  140. (define build
  141. (with-extensions gcrypt-sqlite3&co
  142. (with-imported-modules `(((guix config) => ,(make-config.scm))
  143. ,@(source-module-closure
  144. '((guix build store-copy)
  145. (guix store database))
  146. #:select? not-config?))
  147. #~(begin
  148. (use-modules (guix store database)
  149. (guix build store-copy)
  150. (srfi srfi-1))
  151. (define (read-closure closure)
  152. (call-with-input-file closure read-reference-graph))
  153. (define db-file
  154. (store-database-file #:state-directory #$output))
  155. ;; Make sure non-ASCII file names are properly handled.
  156. (setenv "GUIX_LOCPATH"
  157. #+(file-append glibc-utf8-locales "/lib/locale"))
  158. (setlocale LC_ALL "en_US.utf8")
  159. (sql-schema #$schema)
  160. (let ((items (append-map read-closure '#$labels)))
  161. (with-database db-file db
  162. (register-items db items
  163. #:registration-time %epoch)))))))
  164. (computed-file "store-database" build
  165. #:options `(#:references-graphs ,(zip labels items))))
  166. (define-syntax-rule (define-with-source (variable args ...) body body* ...)
  167. "Bind VARIABLE to a procedure accepting ARGS defined as BODY, also setting
  168. its source property."
  169. (begin
  170. (define (variable args ...)
  171. body body* ...)
  172. (eval-when (load eval)
  173. (set-procedure-property! variable 'source
  174. '(define (variable args ...) body body* ...)))))
  175. (define-with-source (manifest->friendly-name manifest)
  176. "Return a friendly name computed from the entries in MANIFEST, a
  177. <manifest> object."
  178. (let loop ((names (map manifest-entry-name
  179. (manifest-entries manifest))))
  180. (define str (string-join names "-"))
  181. (if (< (string-length str) 40)
  182. str
  183. (match names
  184. ((_) str)
  185. ((names ... _) (loop names))))))
  186. ;;;
  187. ;;; Tarball format.
  188. ;;;
  189. (define* (self-contained-tarball/builder profile
  190. #:key (profile-name "guix-profile")
  191. (compressor (first %compressors))
  192. localstatedir?
  193. (symlinks '())
  194. (archiver tar)
  195. (extra-options '()))
  196. "Return the G-Expression of the builder used for self-contained-tarball."
  197. (define database
  198. (and localstatedir?
  199. (file-append (store-database (list profile))
  200. "/db/db.sqlite")))
  201. (define set-utf8-locale
  202. ;; Arrange to not depend on 'glibc-utf8-locales' when using '--bootstrap'.
  203. (and (or (not (profile? profile))
  204. (profile-locales? profile))
  205. #~(begin
  206. (setenv "GUIX_LOCPATH"
  207. #+(file-append glibc-utf8-locales "/lib/locale"))
  208. (setlocale LC_ALL "en_US.utf8"))))
  209. (define (import-module? module)
  210. ;; Since we don't use deduplication support in 'populate-store', don't
  211. ;; import (guix store deduplication) and its dependencies, which includes
  212. ;; Guile-Gcrypt. That way we can run tests with '--bootstrap'.
  213. (and (not-config? module)
  214. (not (equal? '(guix store deduplication) module))))
  215. (with-imported-modules (source-module-closure
  216. `((guix build pack)
  217. (guix build store-copy)
  218. (guix build utils)
  219. (guix build union)
  220. (gnu build install))
  221. #:select? import-module?)
  222. #~(begin
  223. (use-modules (guix build pack)
  224. (guix build store-copy)
  225. (guix build utils)
  226. ((guix build union) #:select (relative-file-name))
  227. (gnu build install)
  228. (srfi srfi-1)
  229. (srfi srfi-26)
  230. (ice-9 match))
  231. (define %root "root")
  232. (define symlink->directives
  233. ;; Return "populate directives" to make the given symlink and its
  234. ;; parent directories.
  235. (match-lambda
  236. ((source '-> target)
  237. (let ((target (string-append #$profile "/" target))
  238. (parent (dirname source)))
  239. ;; Never add a 'directory' directive for "/" so as to
  240. ;; preserve its ownership when extracting the archive (see
  241. ;; below), and also because this would lead to adding the
  242. ;; same entries twice in the tarball.
  243. `(,@(if (string=? parent "/")
  244. '()
  245. `((directory ,parent)))
  246. (,source
  247. -> ,(relative-file-name parent target)))))))
  248. (define directives
  249. ;; Fully-qualified symlinks.
  250. (append-map symlink->directives '#$symlinks))
  251. ;; Make sure non-ASCII file names are properly handled.
  252. #+set-utf8-locale
  253. (define tar #+(file-append archiver "/bin/tar"))
  254. ;; Note: there is not much to gain here with deduplication and there
  255. ;; is the overhead of the '.links' directory, so turn it off.
  256. ;; Furthermore GNU tar < 1.30 sometimes fails to extract tarballs
  257. ;; with hard links:
  258. ;; <http://lists.gnu.org/archive/html/bug-tar/2017-11/msg00009.html>.
  259. (populate-store (list "profile") %root #:deduplicate? #f)
  260. (when #+localstatedir?
  261. (install-database-and-gc-roots %root #+database #$profile
  262. #:profile-name #$profile-name))
  263. ;; Create SYMLINKS.
  264. (for-each (cut evaluate-populate-directive <> %root)
  265. directives)
  266. ;; Create the tarball.
  267. (with-directory-excursion %root
  268. ;; GNU Tar recurses directories by default. Simply add the whole
  269. ;; current directory, which contains all the generated files so far.
  270. ;; This avoids creating duplicate files in the archives that would
  271. ;; be stored as hard links by GNU Tar.
  272. (apply invoke tar "-cvf" #$output "."
  273. (tar-base-options
  274. #:tar tar
  275. #:compressor #+(and=> compressor compressor-command)))))))
  276. (define* (self-contained-tarball name profile
  277. #:key target
  278. (profile-name "guix-profile")
  279. deduplicate?
  280. entry-point
  281. (compressor (first %compressors))
  282. localstatedir?
  283. (symlinks '())
  284. (archiver tar)
  285. (extra-options '()))
  286. "Return a self-contained tarball containing a store initialized with the
  287. closure of PROFILE, a derivation. The tarball contains /gnu/store; if
  288. LOCALSTATEDIR? is true, it also contains /var/guix, including /var/guix/db
  289. with a properly initialized store database.
  290. SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
  291. added to the pack."
  292. (when entry-point
  293. (warning (G_ "entry point not supported in the '~a' format~%")
  294. 'tarball))
  295. (gexp->derivation
  296. (string-append name ".tar"
  297. (compressor-extension compressor))
  298. (self-contained-tarball/builder profile
  299. #:profile-name profile-name
  300. #:compressor compressor
  301. #:localstatedir? localstatedir?
  302. #:symlinks symlinks
  303. #:archiver archiver)
  304. #:target target
  305. #:references-graphs `(("profile" ,profile))))
  306. ;;;
  307. ;;; Singularity.
  308. ;;;
  309. (define (singularity-environment-file profile)
  310. "Return a shell script that defines the environment variables corresponding
  311. to the search paths of PROFILE."
  312. (define build
  313. (with-extensions (list guile-gcrypt)
  314. (with-imported-modules `(((guix config) => ,(make-config.scm))
  315. ,@(source-module-closure
  316. `((guix profiles)
  317. (guix search-paths))
  318. #:select? not-config?))
  319. #~(begin
  320. (use-modules (guix profiles) (guix search-paths)
  321. (ice-9 match))
  322. (call-with-output-file #$output
  323. (lambda (port)
  324. (for-each (match-lambda
  325. ((spec . value)
  326. (format port "~a=~a~%export ~a~%"
  327. (search-path-specification-variable spec)
  328. value
  329. (search-path-specification-variable spec))))
  330. (profile-search-paths #$profile))))))))
  331. (computed-file "singularity-environment.sh" build))
  332. ;;;
  333. ;;; SquashFS image format.
  334. ;;;
  335. (define* (squashfs-image name profile
  336. #:key target
  337. (profile-name "guix-profile")
  338. (compressor (first %compressors))
  339. entry-point
  340. localstatedir?
  341. (symlinks '())
  342. (archiver squashfs-tools)
  343. (extra-options '()))
  344. "Return a squashfs image containing a store initialized with the closure of
  345. PROFILE, a derivation. The image contains a subset of /gnu/store, empty mount
  346. points for virtual file systems (like procfs), and optional symlinks.
  347. SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
  348. added to the pack."
  349. (define database
  350. (and localstatedir?
  351. (file-append (store-database (list profile))
  352. "/db/db.sqlite")))
  353. (define environment
  354. (singularity-environment-file profile))
  355. (define symlinks*
  356. ;; Singularity requires /bin (specifically /bin/sh), so ensure that
  357. ;; symlink is created.
  358. (if (find (match-lambda
  359. (("/bin" . _) #t)
  360. (_ #f))
  361. symlinks)
  362. symlinks
  363. `(("/bin" -> "bin") ,@symlinks)))
  364. (define build
  365. (with-extensions (list guile-gcrypt)
  366. (with-imported-modules (source-module-closure
  367. '((guix build utils)
  368. (guix build store-copy)
  369. (guix build union)
  370. (gnu build install))
  371. #:select? not-config?)
  372. #~(begin
  373. (use-modules (guix build utils)
  374. (guix build store-copy)
  375. ((guix build union) #:select (relative-file-name))
  376. (gnu build install)
  377. (srfi srfi-1)
  378. (srfi srfi-26)
  379. (ice-9 match))
  380. (define database #+database)
  381. (define entry-point #$entry-point)
  382. (define (mksquashfs args)
  383. (apply invoke "mksquashfs"
  384. `(,@args
  385. ;; Do not create a "recovery file" when appending to the
  386. ;; file system since it's useless in this case.
  387. "-no-recovery"
  388. ;; Do not attempt to store extended attributes.
  389. ;; See <https://bugs.gnu.org/40043>.
  390. "-no-xattrs"
  391. ;; Set file times and the file system creation time to
  392. ;; one second after the Epoch.
  393. "-all-time" "1" "-mkfs-time" "1"
  394. ;; Reset all UIDs and GIDs.
  395. "-force-uid" "0" "-force-gid" "0")))
  396. (setenv "PATH" #+(file-append archiver "/bin"))
  397. ;; We need an empty file in order to have a valid file argument when
  398. ;; we reparent the root file system. Read on for why that's
  399. ;; necessary.
  400. (with-output-to-file ".empty" (lambda () (display "")))
  401. ;; Create the squashfs image in several steps.
  402. ;; Add all store items. Unfortunately mksquashfs throws away all
  403. ;; ancestor directories and only keeps the basename. We fix this
  404. ;; in the following invocations of mksquashfs.
  405. (mksquashfs `(,@(map store-info-item
  406. (call-with-input-file "profile"
  407. read-reference-graph))
  408. #$environment
  409. ,#$output
  410. ;; Do not perform duplicate checking because we
  411. ;; don't have any dupes.
  412. "-no-duplicates"
  413. "-comp"
  414. ,#+(compressor-name compressor)))
  415. ;; Here we reparent the store items. For each sub-directory of
  416. ;; the store prefix we need one invocation of "mksquashfs".
  417. (for-each (lambda (dir)
  418. (mksquashfs `(".empty"
  419. ,#$output
  420. "-root-becomes" ,dir)))
  421. (reverse (string-tokenize (%store-directory)
  422. (char-set-complement (char-set #\/)))))
  423. ;; Add symlinks and mount points.
  424. (mksquashfs
  425. `(".empty"
  426. ,#$output
  427. ;; Create SYMLINKS via pseudo file definitions.
  428. ,@(append-map
  429. (match-lambda
  430. ((source '-> target)
  431. ;; Create relative symlinks to work around a bug in
  432. ;; Singularity 2.x:
  433. ;; https://bugs.gnu.org/34913
  434. ;; https://github.com/sylabs/singularity/issues/1487
  435. (let ((target (string-append #$profile "/" target)))
  436. (list "-p"
  437. (string-join
  438. ;; name s mode uid gid symlink
  439. (list source
  440. "s" "777" "0" "0"
  441. (relative-file-name (dirname source)
  442. target)))))))
  443. '#$symlinks*)
  444. "-p" "/.singularity.d d 555 0 0"
  445. ;; Create the environment file.
  446. "-p" "/.singularity.d/env d 555 0 0"
  447. "-p" ,(string-append
  448. "/.singularity.d/env/90-environment.sh s 777 0 0 "
  449. (relative-file-name "/.singularity.d/env"
  450. #$environment))
  451. ;; Create /.singularity.d/actions, and optionally the 'run'
  452. ;; script, used by 'singularity run'.
  453. "-p" "/.singularity.d/actions d 555 0 0"
  454. ,@(if entry-point
  455. `( ;; This one if for Singularity 2.x.
  456. "-p"
  457. ,(string-append
  458. "/.singularity.d/actions/run s 777 0 0 "
  459. (relative-file-name "/.singularity.d/actions"
  460. (string-append #$profile "/"
  461. entry-point)))
  462. ;; This one is for Singularity 3.x.
  463. "-p"
  464. ,(string-append
  465. "/.singularity.d/runscript s 777 0 0 "
  466. (relative-file-name "/.singularity.d"
  467. (string-append #$profile "/"
  468. entry-point))))
  469. '())
  470. ;; Create empty mount points.
  471. "-p" "/proc d 555 0 0"
  472. "-p" "/sys d 555 0 0"
  473. "-p" "/dev d 555 0 0"
  474. "-p" "/home d 555 0 0"))
  475. (when database
  476. ;; Initialize /var/guix.
  477. (install-database-and-gc-roots "var-etc" database #$profile)
  478. (mksquashfs `("var-etc" ,#$output)))))))
  479. (gexp->derivation (string-append name
  480. (compressor-extension compressor)
  481. ".squashfs")
  482. build
  483. #:target target
  484. #:references-graphs `(("profile" ,profile))))
  485. ;;;
  486. ;;; Docker image format.
  487. ;;;
  488. (define* (docker-image name profile
  489. #:key target
  490. (profile-name "guix-profile")
  491. (compressor (first %compressors))
  492. entry-point
  493. localstatedir?
  494. (symlinks '())
  495. (archiver tar)
  496. (extra-options '()))
  497. "Return a derivation to construct a Docker image of PROFILE. The
  498. image is a tarball conforming to the Docker Image Specification, compressed
  499. with COMPRESSOR. It can be passed to 'docker load'. If TARGET is true, it
  500. must a be a GNU triplet and it is used to derive the architecture metadata in
  501. the image."
  502. (define database
  503. (and localstatedir?
  504. (file-append (store-database (list profile))
  505. "/db/db.sqlite")))
  506. (define defmod 'define-module) ;trick Geiser
  507. (define build
  508. ;; Guile-JSON and Guile-Gcrypt are required by (guix docker).
  509. (with-extensions (list guile-json-3 guile-gcrypt)
  510. (with-imported-modules `(((guix config) => ,(make-config.scm))
  511. ,@(source-module-closure
  512. `((guix docker)
  513. (guix build store-copy)
  514. (guix build utils) ;for %xz-parallel-args
  515. (guix profiles)
  516. (guix search-paths))
  517. #:select? not-config?))
  518. #~(begin
  519. (use-modules (guix docker) (guix build store-copy)
  520. (guix build utils)
  521. (guix profiles) (guix search-paths)
  522. (srfi srfi-1) (srfi srfi-19)
  523. (ice-9 match))
  524. #$(procedure-source manifest->friendly-name)
  525. (define environment
  526. (map (match-lambda
  527. ((spec . value)
  528. (cons (search-path-specification-variable spec)
  529. value)))
  530. (profile-search-paths #$profile)))
  531. (define symlink->directives
  532. ;; Return "populate directives" to make the given symlink and its
  533. ;; parent directories.
  534. (match-lambda
  535. ((source '-> target)
  536. (let ((target (string-append #$profile "/" target))
  537. (parent (dirname source)))
  538. `((directory ,parent)
  539. (,source -> ,target))))))
  540. (define directives
  541. ;; Create a /tmp directory, as some programs expect it, and
  542. ;; create SYMLINKS.
  543. `((directory "/tmp" ,(getuid) ,(getgid) #o1777)
  544. ,@(append-map symlink->directives '#$symlinks)))
  545. (setenv "PATH" #+(file-append archiver "/bin"))
  546. (build-docker-image #$output
  547. (map store-info-item
  548. (call-with-input-file "profile"
  549. read-reference-graph))
  550. #$profile
  551. #:repository (manifest->friendly-name
  552. (profile-manifest #$profile))
  553. #:database #+database
  554. #:system (or #$target %host-type)
  555. #:environment environment
  556. #:entry-point
  557. #$(and entry-point
  558. #~(list (string-append #$profile "/"
  559. #$entry-point)))
  560. #:extra-files directives
  561. #:compressor #+(compressor-command compressor)
  562. #:creation-time (make-time time-utc 0 1))))))
  563. (gexp->derivation (string-append name ".tar"
  564. (compressor-extension compressor))
  565. build
  566. #:target target
  567. #:references-graphs `(("profile" ,profile))))
  568. ;;;
  569. ;;; Debian archive format.
  570. ;;;
  571. ;;; TODO: When relocatable option is selected, install to a unique prefix.
  572. ;;; This would enable installation of multiple deb packs with conflicting
  573. ;;; files at the same time.
  574. (define* (debian-archive name profile
  575. #:key target
  576. (profile-name "guix-profile")
  577. deduplicate?
  578. entry-point
  579. (compressor (first %compressors))
  580. localstatedir?
  581. (symlinks '())
  582. (archiver tar)
  583. (extra-options '()))
  584. "Return a Debian archive (.deb) containing a store initialized with the
  585. closure of PROFILE, a derivation. The archive contains /gnu/store; if
  586. LOCALSTATEDIR? is true, it also contains /var/guix, including /var/guix/db
  587. with a properly initialized store database. The supported compressors are
  588. \"none\", \"gz\" or \"xz\".
  589. SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
  590. added to the pack. EXTRA-OPTIONS may contain the CONFIG-FILE, POSTINST-FILE
  591. or TRIGGERS-FILE keyword arguments."
  592. ;; For simplicity, limit the supported compressors to the superset of
  593. ;; compressors able to compress both the control file (gz or xz) and the
  594. ;; data tarball (gz, bz2 or xz).
  595. (define %valid-compressors '("gzip" "xz" "none"))
  596. (let ((compressor-name (compressor-name compressor)))
  597. (unless (member compressor-name %valid-compressors)
  598. (leave (G_ "~a is not a valid Debian archive compressor. \
  599. Valid compressors are: ~a~%") compressor-name %valid-compressors)))
  600. (when entry-point
  601. (warning (G_ "entry point not supported in the '~a' format~%")
  602. 'deb))
  603. (define data-tarball
  604. (computed-file (string-append "data.tar"
  605. (compressor-extension compressor))
  606. (self-contained-tarball/builder
  607. profile
  608. #:profile-name profile-name
  609. #:compressor compressor
  610. #:localstatedir? localstatedir?
  611. #:symlinks symlinks
  612. #:archiver archiver)
  613. #:local-build? #f ;allow offloading
  614. #:options (list #:references-graphs `(("profile" ,profile))
  615. #:target target)))
  616. (define build
  617. (with-extensions (list guile-gcrypt)
  618. (with-imported-modules `(((guix config) => ,(make-config.scm))
  619. ,@(source-module-closure
  620. `((guix build pack)
  621. (guix build utils)
  622. (guix profiles))
  623. #:select? not-config?))
  624. #~(begin
  625. (use-modules (guix build pack)
  626. (guix build utils)
  627. (guix profiles)
  628. (ice-9 match)
  629. ((oop goops) #:select (get-keyword))
  630. (srfi srfi-1))
  631. (define machine-type
  632. ;; Extract the machine type from the specified target, else from the
  633. ;; current system.
  634. (and=> (or #$target %host-type)
  635. (lambda (triplet)
  636. (first (string-split triplet #\-)))))
  637. (define (gnu-machine-type->debian-machine-type type)
  638. "Translate machine TYPE from the GNU to Debian terminology."
  639. ;; Debian has its own jargon, different from the one used in GNU, for
  640. ;; machine types (see data/cputable in the sources of dpkg).
  641. (match type
  642. ("i486" "i386")
  643. ("i586" "i386")
  644. ("i686" "i386")
  645. ("x86_64" "amd64")
  646. ("aarch64" "arm64")
  647. ("mipsisa32r6" "mipsr6")
  648. ("mipsisa32r6el" "mipsr6el")
  649. ("mipsisa64r6" "mips64r6")
  650. ("mipsisa64r6el" "mips64r6el")
  651. ("powerpcle" "powerpcel")
  652. ("powerpc64" "ppc64")
  653. ("powerpc64le" "ppc64el")
  654. (machine machine)))
  655. (define architecture
  656. (gnu-machine-type->debian-machine-type machine-type))
  657. #$(procedure-source manifest->friendly-name)
  658. (define manifest (profile-manifest #$profile))
  659. (define single-entry ;manifest entry
  660. (match (manifest-entries manifest)
  661. ((entry)
  662. entry)
  663. (() #f)))
  664. (define package-name (or (and=> single-entry manifest-entry-name)
  665. (manifest->friendly-name manifest)))
  666. (define package-version
  667. (or (and=> single-entry manifest-entry-version)
  668. "0.0.0"))
  669. (define debian-format-version "2.0")
  670. ;; Generate the debian-binary file.
  671. (call-with-output-file "debian-binary"
  672. (lambda (port)
  673. (format port "~a~%" debian-format-version)))
  674. (define data-tarball-file-name (strip-store-file-name
  675. #+data-tarball))
  676. (copy-file #+data-tarball data-tarball-file-name)
  677. ;; Generate the control archive.
  678. (define control-file
  679. (get-keyword #:control-file '#$extra-options))
  680. (define postinst-file
  681. (get-keyword #:postinst-file '#$extra-options))
  682. (define triggers-file
  683. (get-keyword #:triggers-file '#$extra-options))
  684. (define control-tarball-file-name
  685. (string-append "control.tar"
  686. #$(compressor-extension compressor)))
  687. ;; Write the compressed control tarball. Only the control file is
  688. ;; mandatory (see: 'man deb' and 'man deb-control').
  689. (if control-file
  690. (copy-file control-file "control")
  691. (call-with-output-file "control"
  692. (lambda (port)
  693. (format port "\
  694. Package: ~a
  695. Version: ~a
  696. Description: Debian archive generated by GNU Guix.
  697. Maintainer: GNU Guix
  698. Architecture: ~a
  699. Priority: optional
  700. Section: misc
  701. ~%" package-name package-version architecture))))
  702. (when postinst-file
  703. (copy-file postinst-file "postinst")
  704. (chmod "postinst" #o755))
  705. (when triggers-file
  706. (copy-file triggers-file "triggers"))
  707. (define tar (string-append #+archiver "/bin/tar"))
  708. (apply invoke tar
  709. `(,@(tar-base-options
  710. #:tar tar
  711. #:compressor #+(and=> compressor compressor-command))
  712. "-cvf" ,control-tarball-file-name
  713. "control"
  714. ,@(if postinst-file '("postinst") '())
  715. ,@(if triggers-file '("triggers") '())))
  716. ;; Create the .deb archive using GNU ar.
  717. (invoke (string-append #+binutils "/bin/ar") "-rv" #$output
  718. "debian-binary"
  719. control-tarball-file-name data-tarball-file-name)))))
  720. (gexp->derivation (string-append name ".deb")
  721. build
  722. #:target target
  723. #:references-graphs `(("profile" ,profile))))
  724. ;;;
  725. ;;; Compiling C programs.
  726. ;;;
  727. ;; A C compiler. That lowers to a single program that can be passed typical C
  728. ;; compiler flags, and it makes sure the whole toolchain is available.
  729. (define-record-type <c-compiler>
  730. (%c-compiler toolchain guile)
  731. c-compiler?
  732. (toolchain c-compiler-toolchain)
  733. (guile c-compiler-guile))
  734. (define* (c-compiler #:optional inputs
  735. #:key (guile (default-guile)))
  736. (%c-compiler inputs guile))
  737. (define (bootstrap-c-compiler)
  738. "Return the C compiler that uses the bootstrap toolchain. This is used only
  739. by '--bootstrap', for testing purposes."
  740. (define bootstrap-toolchain
  741. (list (first (assoc-ref (%bootstrap-inputs) "gcc"))
  742. (first (assoc-ref (%bootstrap-inputs) "binutils"))
  743. (first (assoc-ref (%bootstrap-inputs) "libc"))))
  744. (c-compiler bootstrap-toolchain
  745. #:guile %bootstrap-guile))
  746. (define-gexp-compiler (c-compiler-compiler (compiler <c-compiler>) system target)
  747. "Lower COMPILER to a single script that does the right thing."
  748. (define toolchain
  749. (or (c-compiler-toolchain compiler)
  750. (list (first (assoc-ref (standard-packages) "gcc"))
  751. (first (assoc-ref (standard-packages) "ld-wrapper"))
  752. (first (assoc-ref (standard-packages) "binutils"))
  753. (first (assoc-ref (standard-packages) "libc"))
  754. (gexp-input (first (assoc-ref (standard-packages) "libc"))
  755. "static"))))
  756. (define inputs
  757. (match (append-map package-propagated-inputs
  758. (filter package? toolchain))
  759. (((labels things . _) ...)
  760. (append toolchain things))))
  761. (define search-paths
  762. (cons $PATH
  763. (append-map package-native-search-paths
  764. (filter package? inputs))))
  765. (define run
  766. (with-imported-modules (source-module-closure
  767. '((guix build utils)
  768. (guix search-paths)))
  769. #~(begin
  770. (use-modules (guix build utils) (guix search-paths)
  771. (ice-9 match))
  772. (define (output-file args)
  773. (let loop ((args args))
  774. (match args
  775. (() "a.out")
  776. (("-o" file _ ...) file)
  777. ((head rest ...) (loop rest)))))
  778. (set-search-paths (map sexp->search-path-specification
  779. '#$(map search-path-specification->sexp
  780. search-paths))
  781. '#$inputs)
  782. (let ((output (output-file (command-line))))
  783. (apply invoke "gcc" (cdr (command-line)))
  784. (invoke "strip" output)))))
  785. (when target
  786. ;; TODO: Yep, we'll have to do it someday!
  787. (leave (G_ "cross-compilation not implemented here;
  788. please email '~a'~%")
  789. (@ (guix config) %guix-bug-report-address)))
  790. (gexp->script "c-compiler" run
  791. #:guile (c-compiler-guile compiler)))
  792. ;;;
  793. ;;; Wrapped package.
  794. ;;;
  795. (define* (wrapped-package package
  796. #:optional
  797. (output* "out")
  798. (compiler (c-compiler))
  799. #:key proot?)
  800. "Return the OUTPUT of PACKAGE with its binaries wrapped such that they are
  801. relocatable. When PROOT? is true, include PRoot in the result and use it as a
  802. last resort for relocation."
  803. (define runner
  804. (local-file (search-auxiliary-file "run-in-namespace.c")))
  805. (define audit-source
  806. (local-file (search-auxiliary-file "pack-audit.c")))
  807. (define (proot)
  808. (specification->package "proot-static"))
  809. (define (fakechroot-library)
  810. (computed-file "libfakechroot.so"
  811. #~(copy-file #$(file-append
  812. (specification->package "fakechroot")
  813. "/lib/fakechroot/libfakechroot.so")
  814. #$output)))
  815. (define (audit-module)
  816. ;; Return an ld.so audit module for use by the 'fakechroot' execution
  817. ;; engine that translates file names of all the files ld.so loads.
  818. (computed-file "pack-audit.so"
  819. (with-imported-modules '((guix build utils))
  820. #~(begin
  821. (use-modules (guix build utils))
  822. (copy-file #$audit-source "audit.c")
  823. (substitute* "audit.c"
  824. (("@STORE_DIRECTORY@")
  825. (%store-directory)))
  826. (invoke #$compiler "-std=gnu99"
  827. "-shared" "-fPIC" "-Os" "-g0"
  828. "-Wall" "audit.c" "-o" #$output)))))
  829. (define build
  830. (with-imported-modules (source-module-closure
  831. '((guix build utils)
  832. (guix build union)
  833. (guix build gremlin)
  834. (guix elf)))
  835. #~(begin
  836. (use-modules (guix build utils)
  837. ((guix build union) #:select (symlink-relative))
  838. (guix elf)
  839. (guix build gremlin)
  840. (ice-9 binary-ports)
  841. (ice-9 ftw)
  842. (ice-9 match)
  843. (ice-9 receive)
  844. (srfi srfi-1)
  845. (rnrs bytevectors))
  846. (define input
  847. ;; The OUTPUT* output of PACKAGE.
  848. (ungexp package output*))
  849. (define target
  850. ;; The output we are producing.
  851. (ungexp output output*))
  852. (define (strip-store-prefix file)
  853. ;; Given a file name like "/gnu/store/…-foo-1.2/bin/foo", return
  854. ;; "/bin/foo".
  855. (let* ((len (string-length (%store-directory)))
  856. (base (string-drop file (+ 1 len))))
  857. (match (string-index base #\/)
  858. (#f base)
  859. (index (string-drop base index)))))
  860. (define (elf-interpreter elf)
  861. ;; Return the interpreter of ELF as a string, or #f if ELF has no
  862. ;; interpreter segment.
  863. (match (find (lambda (segment)
  864. (= (elf-segment-type segment) PT_INTERP))
  865. (elf-segments elf))
  866. (#f #f) ;maybe a .so
  867. (segment
  868. (let ((bv (make-bytevector (- (elf-segment-memsz segment) 1))))
  869. (bytevector-copy! (elf-bytes elf)
  870. (elf-segment-offset segment)
  871. bv 0 (bytevector-length bv))
  872. (utf8->string bv)))))
  873. (define (runpath file)
  874. ;; Return the RUNPATH of FILE as a list of directories.
  875. (let* ((bv (call-with-input-file file get-bytevector-all))
  876. (elf (parse-elf bv))
  877. (dyninfo (elf-dynamic-info elf)))
  878. (or (and=> dyninfo elf-dynamic-info-runpath)
  879. '())))
  880. (define (elf-loader-compile-flags program)
  881. ;; Return the cpp flags defining macros for the ld.so/fakechroot
  882. ;; wrapper of PROGRAM.
  883. ;; TODO: Handle scripts by wrapping their interpreter.
  884. (if (elf-file? program)
  885. (let* ((bv (call-with-input-file program
  886. get-bytevector-all))
  887. (elf (parse-elf bv))
  888. (interp (elf-interpreter elf))
  889. (gconv (and interp
  890. (string-append (dirname interp)
  891. "/gconv"))))
  892. (if interp
  893. (list (string-append "-DPROGRAM_INTERPRETER=\""
  894. interp "\"")
  895. (string-append "-DFAKECHROOT_LIBRARY=\""
  896. #$(fakechroot-library) "\"")
  897. (string-append "-DLOADER_AUDIT_MODULE=\""
  898. #$(audit-module) "\"")
  899. ;; XXX: Normally (runpath #$(audit-module)) is
  900. ;; enough. However, to work around
  901. ;; <https://sourceware.org/bugzilla/show_bug.cgi?id=26634>
  902. ;; (glibc <= 2.32), pass the whole search path of
  903. ;; PROGRAM, which presumably is a superset of that
  904. ;; of the audit module.
  905. (string-append "-DLOADER_AUDIT_RUNPATH={ "
  906. (string-join
  907. (map object->string
  908. (runpath program))
  909. ", " 'suffix)
  910. "NULL }")
  911. (if gconv
  912. (string-append "-DGCONV_DIRECTORY=\""
  913. gconv "\"")
  914. "-UGCONV_DIRECTORY"))
  915. '()))
  916. '()))
  917. (define (build-wrapper program)
  918. ;; Build a user-namespace wrapper for PROGRAM.
  919. (format #t "building wrapper for '~a'...~%" program)
  920. (copy-file #$runner "run.c")
  921. (substitute* "run.c"
  922. (("@WRAPPED_PROGRAM@") program)
  923. (("@STORE_DIRECTORY@") (%store-directory)))
  924. (let* ((base (strip-store-prefix program))
  925. (result (string-append target base))
  926. (proot #$(and proot?
  927. #~(string-drop
  928. #$(file-append (proot) "/bin/proot")
  929. (+ (string-length (%store-directory))
  930. 1)))))
  931. (mkdir-p (dirname result))
  932. (apply invoke #$compiler "-std=gnu99" "-static" "-Os" "-g0" "-Wall"
  933. "run.c" "-o" result
  934. (string-append "-DWRAPPER_PROGRAM=\""
  935. (canonicalize-path (dirname result)) "/"
  936. (basename result) "\"")
  937. (append (if proot
  938. (list (string-append "-DPROOT_PROGRAM=\""
  939. proot "\""))
  940. '())
  941. (elf-loader-compile-flags program)))
  942. (delete-file "run.c")))
  943. (setvbuf (current-output-port) 'line)
  944. ;; Link the top-level files of PACKAGE so that search paths are
  945. ;; properly defined in PROFILE/etc/profile.
  946. (mkdir target)
  947. (for-each (lambda (file)
  948. (unless (member file '("." ".." "bin" "sbin" "libexec"))
  949. (symlink-relative (string-append input "/" file)
  950. (string-append target "/" file))))
  951. (scandir input))
  952. (receive (executables others)
  953. (partition executable-file?
  954. ;; Note: Trailing slash in case these are symlinks.
  955. (append (find-files (string-append input "/bin/"))
  956. (find-files (string-append input "/sbin/"))
  957. (find-files (string-append input "/libexec/"))))
  958. ;; Wrap only executables, since the wrapper will eventually need
  959. ;; to execve them. E.g. git's "libexec" directory contains many
  960. ;; shell scripts that are source'd from elsewhere, which fails if
  961. ;; they are wrapped.
  962. (for-each build-wrapper executables)
  963. ;; Link any other non-executable files
  964. (for-each (lambda (old)
  965. (let ((new (string-append target (strip-store-prefix old))))
  966. (mkdir-p (dirname new))
  967. (symlink-relative old new)))
  968. others)))))
  969. (computed-file (string-append
  970. (cond ((package? package)
  971. (package-full-name package "-"))
  972. ((inferior-package? package)
  973. (string-append (inferior-package-name package)
  974. "-"
  975. (inferior-package-version package)))
  976. (else "wrapper"))
  977. "R")
  978. build))
  979. (define (wrapped-manifest-entry entry . args)
  980. (manifest-entry
  981. (inherit entry)
  982. (item (apply wrapped-package
  983. (manifest-entry-item entry)
  984. (manifest-entry-output entry)
  985. args))
  986. (dependencies (map (lambda (entry)
  987. (apply wrapped-manifest-entry entry args))
  988. (manifest-entry-dependencies entry)))))
  989. ;;;
  990. ;;; Command-line options.
  991. ;;;
  992. (define %default-options
  993. ;; Alist of default option values.
  994. `((format . tarball)
  995. (profile-name . "guix-profile")
  996. (system . ,(%current-system))
  997. (substitutes? . #t)
  998. (offload? . #t)
  999. (graft? . #t)
  1000. (print-build-trace? . #t)
  1001. (print-extended-build-trace? . #t)
  1002. (multiplexed-build-output? . #t)
  1003. (debug . 0)
  1004. (verbosity . 1)
  1005. (symlinks . ())
  1006. (compressor . ,(first %compressors))))
  1007. (define %formats
  1008. ;; Supported pack formats.
  1009. `((tarball . ,self-contained-tarball)
  1010. (squashfs . ,squashfs-image)
  1011. (docker . ,docker-image)
  1012. (deb . ,debian-archive)))
  1013. (define (show-formats)
  1014. ;; Print the supported pack formats.
  1015. (display (G_ "The supported formats for 'guix pack' are:"))
  1016. (newline)
  1017. (display (G_ "
  1018. tarball Self-contained tarball, ready to run on another machine"))
  1019. (display (G_ "
  1020. squashfs Squashfs image suitable for Singularity"))
  1021. (display (G_ "
  1022. docker Tarball ready for 'docker load'"))
  1023. (display (G_ "
  1024. deb Debian archive installable via dpkg/apt"))
  1025. (newline))
  1026. (define %deb-format-options
  1027. (let ((required-option (lambda (symbol)
  1028. (option (list (symbol->string symbol)) #t #f
  1029. (lambda (opt name arg result . rest)
  1030. (apply values
  1031. (alist-cons symbol arg result)
  1032. rest))))))
  1033. (list (required-option 'control-file)
  1034. (required-option 'postinst-file)
  1035. (required-option 'triggers-file))))
  1036. (define (show-deb-format-options)
  1037. (display (G_ "
  1038. --help-deb-format list options specific to the deb format")))
  1039. (define (show-deb-format-options/detailed)
  1040. (display (G_ "
  1041. --control-file=FILE
  1042. Embed the provided control FILE"))
  1043. (display (G_ "
  1044. --postinst-file=FILE
  1045. Embed the provided postinst script"))
  1046. (display (G_ "
  1047. --triggers-file=FILE
  1048. Embed the provided triggers FILE"))
  1049. (newline)
  1050. (exit 0))
  1051. (define %options
  1052. ;; Specifications of the command-line options.
  1053. (cons* (option '(#\h "help") #f #f
  1054. (lambda args
  1055. (show-help)
  1056. (exit 0)))
  1057. (option '(#\V "version") #f #f
  1058. (lambda args
  1059. (show-version-and-exit "guix pack")))
  1060. (option '(#\n "dry-run") #f #f
  1061. (lambda (opt name arg result)
  1062. (alist-cons 'dry-run? #t result)))
  1063. (option '(#\d "derivation") #f #f
  1064. (lambda (opt name arg result)
  1065. (alist-cons 'derivation-only? #t result)))
  1066. (option '(#\f "format") #t #f
  1067. (lambda (opt name arg result)
  1068. (alist-cons 'format (string->symbol arg) result)))
  1069. (option '("list-formats") #f #f
  1070. (lambda args
  1071. (show-formats)
  1072. (exit 0)))
  1073. (option '(#\R "relocatable") #f #f
  1074. (lambda (opt name arg result)
  1075. (match (assq-ref result 'relocatable?)
  1076. (#f
  1077. (alist-cons 'relocatable? #t result))
  1078. (_
  1079. (alist-cons 'relocatable? 'proot
  1080. (alist-delete 'relocatable? result))))))
  1081. (option '(#\e "expression") #t #f
  1082. (lambda (opt name arg result)
  1083. (alist-cons 'expression arg result)))
  1084. (option '(#\m "manifest") #t #f
  1085. (lambda (opt name arg result)
  1086. (alist-cons 'manifest arg result)))
  1087. (option '(#\s "system") #t #f
  1088. (lambda (opt name arg result)
  1089. (alist-cons 'system arg
  1090. (alist-delete 'system result eq?))))
  1091. (option '("entry-point") #t #f
  1092. (lambda (opt name arg result)
  1093. (alist-cons 'entry-point arg result)))
  1094. (option '("target") #t #f
  1095. (lambda (opt name arg result)
  1096. (alist-cons 'target arg
  1097. (alist-delete 'target result eq?))))
  1098. (option '(#\C "compression") #t #f
  1099. (lambda (opt name arg result)
  1100. (alist-cons 'compressor (lookup-compressor arg)
  1101. result)))
  1102. (option '(#\S "symlink") #t #f
  1103. (lambda (opt name arg result)
  1104. ;; Note: Using 'string-split' allows us to handle empty
  1105. ;; TARGET (as in "/opt/guile=", meaning that /opt/guile is
  1106. ;; a symlink to the profile) correctly.
  1107. (match (string-split arg (char-set #\=))
  1108. ((source target)
  1109. (let ((symlinks (assoc-ref result 'symlinks)))
  1110. (alist-cons 'symlinks
  1111. `((,source -> ,target) ,@symlinks)
  1112. (alist-delete 'symlinks result eq?))))
  1113. (x
  1114. (leave (G_ "~a: invalid symlink specification~%")
  1115. arg)))))
  1116. (option '("save-provenance") #f #f
  1117. (lambda (opt name arg result)
  1118. (alist-cons 'save-provenance? #t result)))
  1119. (option '("localstatedir") #f #f
  1120. (lambda (opt name arg result)
  1121. (alist-cons 'localstatedir? #t result)))
  1122. (option '("profile-name") #t #f
  1123. (lambda (opt name arg result)
  1124. (match arg
  1125. ((or "guix-profile" "current-guix")
  1126. (alist-cons 'profile-name arg result))
  1127. (_
  1128. (leave (G_ "~a: unsupported profile name~%") arg)))))
  1129. (option '(#\r "root") #t #f
  1130. (lambda (opt name arg result)
  1131. (alist-cons 'gc-root arg result)))
  1132. (option '(#\v "verbosity") #t #f
  1133. (lambda (opt name arg result)
  1134. (let ((level (string->number* arg)))
  1135. (alist-cons 'verbosity level
  1136. (alist-delete 'verbosity result)))))
  1137. (option '("bootstrap") #f #f
  1138. (lambda (opt name arg result)
  1139. (alist-cons 'bootstrap? #t result)))
  1140. (option '("help-deb-format") #f #f
  1141. (lambda args
  1142. (show-deb-format-options/detailed)))
  1143. (append %deb-format-options
  1144. %transformation-options
  1145. %standard-build-options)))
  1146. (define (show-help)
  1147. (display (G_ "Usage: guix pack [OPTION]... PACKAGE...
  1148. Create a bundle of PACKAGE.\n"))
  1149. (show-build-options-help)
  1150. (newline)
  1151. (show-transformation-options-help)
  1152. (newline)
  1153. (show-deb-format-options)
  1154. (newline)
  1155. (display (G_ "
  1156. -f, --format=FORMAT build a pack in the given FORMAT"))
  1157. (display (G_ "
  1158. --list-formats list the formats available"))
  1159. (display (G_ "
  1160. -R, --relocatable produce relocatable executables"))
  1161. (display (G_ "
  1162. -e, --expression=EXPR consider the package EXPR evaluates to"))
  1163. (display (G_ "
  1164. -s, --system=SYSTEM attempt to build for SYSTEM--e.g., \"i686-linux\""))
  1165. (display (G_ "
  1166. --target=TRIPLET cross-build for TRIPLET--e.g., \"armel-linux-gnu\""))
  1167. (display (G_ "
  1168. -C, --compression=TOOL compress using TOOL--e.g., \"lzip\""))
  1169. (display (G_ "
  1170. -S, --symlink=SPEC create symlinks to the profile according to SPEC"))
  1171. (display (G_ "
  1172. -m, --manifest=FILE create a pack with the manifest from FILE"))
  1173. (display (G_ "
  1174. --entry-point=PROGRAM
  1175. use PROGRAM as the entry point of the pack"))
  1176. (display (G_ "
  1177. --save-provenance save provenance information"))
  1178. (display (G_ "
  1179. --localstatedir include /var/guix in the resulting pack"))
  1180. (display (G_ "
  1181. --profile-name=NAME
  1182. populate /var/guix/profiles/.../NAME"))
  1183. (display (G_ "
  1184. -r, --root=FILE make FILE a symlink to the result, and register it
  1185. as a garbage collector root"))
  1186. (display (G_ "
  1187. -d, --derivation return the derivation of the pack"))
  1188. (display (G_ "
  1189. -v, --verbosity=LEVEL use the given verbosity LEVEL"))
  1190. (display (G_ "
  1191. --bootstrap use the bootstrap binaries to build the pack"))
  1192. (newline)
  1193. (display (G_ "
  1194. -h, --help display this help and exit"))
  1195. (display (G_ "
  1196. -V, --version display version information and exit"))
  1197. (newline)
  1198. (show-bug-report-information))
  1199. ;;;
  1200. ;;; Entry point.
  1201. ;;;
  1202. (define-command (guix-pack . args)
  1203. (category development)
  1204. (synopsis "create application bundles")
  1205. (define opts
  1206. (parse-command-line args %options (list %default-options)))
  1207. (define maybe-package-argument
  1208. ;; Given an option pair, return a package, a package/output tuple, or #f.
  1209. (match-lambda
  1210. (('argument . spec)
  1211. (call-with-values
  1212. (lambda ()
  1213. (specification->package+output spec))
  1214. list))
  1215. (('expression . exp)
  1216. (read/eval-package-expression exp))
  1217. (x #f)))
  1218. (define (manifest-from-args store opts)
  1219. (let* ((transform (options->transformation opts))
  1220. (packages (map (match-lambda
  1221. (((? package? package) output)
  1222. (list (transform package) output))
  1223. ((? package? package)
  1224. (list (transform package) "out")))
  1225. (reverse
  1226. (filter-map maybe-package-argument opts))))
  1227. (manifests (filter-map (match-lambda
  1228. (('manifest . file) file)
  1229. (_ #f))
  1230. opts)))
  1231. (define with-provenance
  1232. (if (assoc-ref opts 'save-provenance?)
  1233. (lambda (manifest)
  1234. (map-manifest-entries
  1235. (lambda (entry)
  1236. (let ((entry (manifest-entry-with-provenance entry)))
  1237. (unless (assq 'provenance (manifest-entry-properties entry))
  1238. (warning (G_ "could not determine provenance of package ~a~%")
  1239. (manifest-entry-name entry)))
  1240. entry))
  1241. manifest))
  1242. identity))
  1243. (with-provenance
  1244. (cond
  1245. ((and (not (null? manifests)) (not (null? packages)))
  1246. (leave (G_ "both a manifest and a package list were given~%")))
  1247. ((not (null? manifests))
  1248. (concatenate-manifests
  1249. (map (lambda (file)
  1250. (let ((user-module (make-user-module
  1251. '((guix profiles) (gnu)))))
  1252. (load* file user-module)))
  1253. manifests)))
  1254. (else
  1255. (packages->manifest packages))))))
  1256. (define (process-file-arg opts name)
  1257. ;; Validate that the file exists and return it as a <local-file> object,
  1258. ;; else #f.
  1259. (let ((value (assoc-ref opts name)))
  1260. (match value
  1261. ((and (? string?) (not (? file-exists?)))
  1262. (leave (G_ "file provided with option ~a does not exist: ~a~%")
  1263. (string-append "--" (symbol->string name)) value))
  1264. ((? string?)
  1265. (local-file value))
  1266. (#f #f))))
  1267. (with-error-handling
  1268. (with-store store
  1269. (with-status-verbosity (assoc-ref opts 'verbosity)
  1270. ;; Set the build options before we do anything else.
  1271. (set-build-options-from-command-line store opts)
  1272. (with-build-handler (build-notifier #:dry-run?
  1273. (assoc-ref opts 'dry-run?)
  1274. #:verbosity
  1275. (assoc-ref opts 'verbosity)
  1276. #:use-substitutes?
  1277. (assoc-ref opts 'substitutes?))
  1278. (parameterize ((%graft? (assoc-ref opts 'graft?))
  1279. (%guile-for-build (package-derivation
  1280. store
  1281. (if (assoc-ref opts 'bootstrap?)
  1282. %bootstrap-guile
  1283. (default-guile))
  1284. (assoc-ref opts 'system)
  1285. #:graft? (assoc-ref opts 'graft?))))
  1286. (let* ((derivation? (assoc-ref opts 'derivation-only?))
  1287. (relocatable? (assoc-ref opts 'relocatable?))
  1288. (proot? (eq? relocatable? 'proot))
  1289. (manifest (let ((manifest (manifest-from-args store opts)))
  1290. ;; Note: We cannot honor '--bootstrap' here because
  1291. ;; 'glibc-bootstrap' lacks 'libc.a'.
  1292. (if relocatable?
  1293. (map-manifest-entries
  1294. (cut wrapped-manifest-entry <> #:proot? proot?)
  1295. manifest)
  1296. manifest)))
  1297. (pack-format (assoc-ref opts 'format))
  1298. (extra-options (match pack-format
  1299. ('deb
  1300. (list #:control-file
  1301. (process-file-arg opts 'control-file)
  1302. #:postinst-file
  1303. (process-file-arg opts 'postinst-file)
  1304. #:triggers-file
  1305. (process-file-arg opts 'triggers-file)))
  1306. (_ '())))
  1307. (target (assoc-ref opts 'target))
  1308. (bootstrap? (assoc-ref opts 'bootstrap?))
  1309. (compressor (if bootstrap?
  1310. bootstrap-xz
  1311. (assoc-ref opts 'compressor)))
  1312. (archiver (if (equal? pack-format 'squashfs)
  1313. squashfs-tools
  1314. (if bootstrap?
  1315. %bootstrap-coreutils&co
  1316. tar)))
  1317. (symlinks (assoc-ref opts 'symlinks))
  1318. (build-image (match (assq-ref %formats pack-format)
  1319. ((? procedure? proc) proc)
  1320. (#f
  1321. (leave (G_ "~a: unknown pack format~%")
  1322. pack-format))))
  1323. (localstatedir? (assoc-ref opts 'localstatedir?))
  1324. (entry-point (assoc-ref opts 'entry-point))
  1325. (profile-name (assoc-ref opts 'profile-name))
  1326. (gc-root (assoc-ref opts 'gc-root))
  1327. (profile (profile
  1328. (content manifest)
  1329. ;; Always produce relative symlinks for
  1330. ;; Singularity (see
  1331. ;; <https://bugs.gnu.org/34913>).
  1332. (relative-symlinks?
  1333. (or relocatable?
  1334. (eq? 'squashfs pack-format)))
  1335. (hooks (if bootstrap?
  1336. '()
  1337. %default-profile-hooks))
  1338. (locales? (not bootstrap?))))
  1339. (name (string-append (manifest->friendly-name manifest)
  1340. "-" (symbol->string pack-format)
  1341. "-pack")))
  1342. (define (lookup-package package)
  1343. (manifest-lookup manifest (manifest-pattern (name package))))
  1344. (when (null? (manifest-entries manifest))
  1345. (warning (G_ "no packages specified; building an empty pack~%")))
  1346. (when (and (eq? pack-format 'squashfs)
  1347. (not (any lookup-package '("bash" "bash-minimal"))))
  1348. (warning (G_ "Singularity requires you to provide a shell~%"))
  1349. (display-hint (G_ "Add @code{bash} or @code{bash-minimal} \
  1350. to your package list.")))
  1351. (run-with-store store
  1352. (mlet* %store-monad ((drv (build-image name profile
  1353. #:target
  1354. target
  1355. #:compressor
  1356. compressor
  1357. #:symlinks
  1358. symlinks
  1359. #:localstatedir?
  1360. localstatedir?
  1361. #:entry-point
  1362. entry-point
  1363. #:profile-name
  1364. profile-name
  1365. #:archiver
  1366. archiver
  1367. #:extra-options
  1368. extra-options)))
  1369. (mbegin %store-monad
  1370. (mwhen derivation?
  1371. (return (format #t "~a~%"
  1372. (derivation-file-name drv))))
  1373. (munless derivation?
  1374. (built-derivations (list drv))
  1375. (mwhen gc-root
  1376. (register-root* (match (derivation->output-paths drv)
  1377. (((names . items) ...)
  1378. items))
  1379. gc-root))
  1380. (return (format #t "~a~%"
  1381. (derivation->output-path drv))))))
  1382. #:target target
  1383. #:system (assoc-ref opts 'system)))))))))