summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxim Cournoyer <maxim.cournoyer@gmail.com>2021-07-02 22:47:51 -0400
committerMaxim Cournoyer <maxim.cournoyer@gmail.com>2021-07-18 02:02:34 -0400
commitaeded14b8342c1e72afd014a1bc121770f8c3a1c (patch)
tree571cee7dff06d9f9af5e1207780dfe3634b13ce8
parent15b4372b6019fa515c5ef01bd290145a8d507000 (diff)
pack: Allow embedding custom control files in deb packs.
* guix/scripts/pack.scm (self-contained-tarball/builder) [extra-options]: New argument. (self-contained-tarball, squashfs-image, docker-image) (debian-archive): Likewise. Remove two TODO comments. Document EXTRA-OPTIONS. Use the custom control files when provided. (%deb-format-options): New variable. (show-deb-format-options, show-deb-format-options/detailed): New procedures. (%options): Register new options. (show-help): Augment with new usage. (guix-pack): Validate and propagate new argument values. * doc/guix.texi (Invoking guix pack)[deb]: Document how to list advanced options. Add an example. * tests/pack.scm (deb archive...): Provide extra-options to the debian-archive procedure, and validate that the provided files are embedded in the pack.
-rw-r--r--doc/guix.texi8
-rw-r--r--guix/scripts/pack.scm121
-rw-r--r--tests/pack.scm27
3 files changed, 133 insertions, 23 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index cca46218f2..b3c16e6507 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -6047,6 +6047,14 @@ such file or directory'' message.
This produces a Debian archive (a package with the @samp{.deb} file
extension) containing all the specified binaries and symbolic links,
that can be installed on top of any dpkg-based GNU(/Linux) distribution.
+Advanced options can be revealed via the @option{--help-deb-format}
+option. They allow embedding control files for more fine-grained
+control, such as activating specific triggers or providing a maintainer
+configure script to run arbitrary setup code upon installation.
+
+@example
+guix pack -f deb -C xz -S /usr/bin/hello=bin/hello hello
+@end example
@quotation Note
Because archives produced with @command{guix pack} contain a collection
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index 6d8b70d1c7..6a8d49e042 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -205,7 +205,8 @@ its source property."
(compressor (first %compressors))
localstatedir?
(symlinks '())
- (archiver tar))
+ (archiver tar)
+ (extra-options '()))
"Return the G-Expression of the builder used for self-contained-tarball."
(define database
(and localstatedir?
@@ -324,7 +325,8 @@ its source property."
(compressor (first %compressors))
localstatedir?
(symlinks '())
- (archiver tar))
+ (archiver tar)
+ (extra-options '()))
"Return a self-contained tarball containing a store initialized with the
closure of PROFILE, a derivation. The tarball contains /gnu/store; if
LOCALSTATEDIR? is true, it also contains /var/guix, including /var/guix/db
@@ -389,7 +391,8 @@ to the search paths of PROFILE."
entry-point
localstatedir?
(symlinks '())
- (archiver squashfs-tools))
+ (archiver squashfs-tools)
+ (extra-options '()))
"Return a squashfs image containing a store initialized with the closure of
PROFILE, a derivation. The image contains a subset of /gnu/store, empty mount
points for virtual file systems (like procfs), and optional symlinks.
@@ -567,7 +570,8 @@ added to the pack."
entry-point
localstatedir?
(symlinks '())
- (archiver tar))
+ (archiver tar)
+ (extra-options '()))
"Return a derivation to construct a Docker image of PROFILE. The
image is a tarball conforming to the Docker Image Specification, compressed
with COMPRESSOR. It can be passed to 'docker load'. If TARGET is true, it
@@ -654,8 +658,6 @@ the image."
;;; TODO: When relocatable option is selected, install to a unique prefix.
;;; This would enable installation of multiple deb packs with conflicting
;;; files at the same time.
-;;; TODO: Allow passing a custom control file from the CLI.
-;;; TODO: Allow providing a postinst script.
(define* (debian-archive name profile
#:key target
(profile-name "guix-profile")
@@ -664,7 +666,8 @@ the image."
(compressor (first %compressors))
localstatedir?
(symlinks '())
- (archiver tar))
+ (archiver tar)
+ (extra-options '()))
"Return a Debian archive (.deb) containing a store initialized with the
closure of PROFILE, a derivation. The archive contains /gnu/store; if
LOCALSTATEDIR? is true, it also contains /var/guix, including /var/guix/db
@@ -672,7 +675,8 @@ with a properly initialized store database. The supported compressors are
\"none\", \"gz\" or \"xz\".
SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
-added to the pack."
+added to the pack. EXTRA-OPTIONS may contain the CONFIG-FILE, POSTINST-FILE
+or TRIGGERS-FILE keyword arguments."
;; For simplicity, limit the supported compressors to the superset of
;; compressors able to compress both the control file (gz or xz) and the
;; data tarball (gz, bz2 or xz).
@@ -714,21 +718,23 @@ Valid compressors are: ~a~%") compressor-name %valid-compressors)))
(guix build utils)
(guix profiles)
(ice-9 match)
+ ((oop goops) #:select (get-keyword))
(srfi srfi-1))
(define machine-type
;; Extract the machine type from the specified target, else from the
;; current system.
- (and=> (or #$target %host-type) (lambda (triplet)
- (first (string-split triplet #\-)))))
+ (and=> (or #$target %host-type)
+ (lambda (triplet)
+ (first (string-split triplet #\-)))))
(define (gnu-machine-type->debian-machine-type type)
"Translate machine TYPE from the GNU to Debian terminology."
;; Debian has its own jargon, different from the one used in GNU, for
;; machine types (see data/cputable in the sources of dpkg).
(match type
- ("i586" "i386")
("i486" "i386")
+ ("i586" "i386")
("i686" "i386")
("x86_64" "amd64")
("aarch64" "arm64")
@@ -773,21 +779,40 @@ Valid compressors are: ~a~%") compressor-name %valid-compressors)))
(copy-file #+data-tarball data-tarball-file-name)
+ ;; Generate the control archive.
+ (define control-file
+ (get-keyword #:control-file '#$extra-options))
+
+ (define postinst-file
+ (get-keyword #:postinst-file '#$extra-options))
+
+ (define triggers-file
+ (get-keyword #:triggers-file '#$extra-options))
+
(define control-tarball-file-name
(string-append "control.tar"
#$(compressor-extension compressor)))
;; Write the compressed control tarball. Only the control file is
;; mandatory (see: 'man deb' and 'man deb-control').
- (call-with-output-file "control"
- (lambda (port)
- (format port "\
+ (if control-file
+ (copy-file control-file "control")
+ (call-with-output-file "control"
+ (lambda (port)
+ (format port "\
Package: ~a
Version: ~a
Description: Debian archive generated by GNU Guix.
Maintainer: GNU Guix
Architecture: ~a
-~%" package-name package-version architecture)))
+~%" package-name package-version architecture))))
+
+ (when postinst-file
+ (copy-file postinst-file "postinst")
+ (chmod "postinst" #o755))
+
+ (when triggers-file
+ (copy-file triggers-file "triggers"))
(define tar (string-append #+archiver "/bin/tar"))
@@ -796,7 +821,9 @@ Architecture: ~a
#:tar tar
#:compressor '#+(and=> compressor compressor-command))
"-cvf" ,control-tarball-file-name
- "control"))
+ "control"
+ ,@(if postinst-file '("postinst") '())
+ ,@(if triggers-file '("triggers") '())))
;; Create the .deb archive using GNU ar.
(invoke (string-append #+binutils "/bin/ar") "-rv" #$output
@@ -1157,6 +1184,34 @@ last resort for relocation."
deb Debian archive installable via dpkg/apt"))
(newline))
+(define %deb-format-options
+ (let ((required-option (lambda (symbol)
+ (option (list (symbol->string symbol)) #t #f
+ (lambda (opt name arg result . rest)
+ (apply values
+ (alist-cons symbol arg result)
+ rest))))))
+ (list (required-option 'control-file)
+ (required-option 'postinst-file)
+ (required-option 'triggers-file))))
+
+(define (show-deb-format-options)
+ (display (G_ "
+ --help-deb-format list options specific to the deb format")))
+
+(define (show-deb-format-options/detailed)
+ (display (G_ "
+ --control-file=FILE
+ Embed the provided control FILE"))
+ (display (G_ "
+ --postinst-file=FILE
+ Embed the provided postinst script"))
+ (display (G_ "
+ --triggers-file=FILE
+ Embed the provided triggers FILE"))
+ (newline)
+ (exit 0))
+
(define %options
;; Specifications of the command-line options.
(cons* (option '(#\h "help") #f #f
@@ -1250,7 +1305,12 @@ last resort for relocation."
(lambda (opt name arg result)
(alist-cons 'bootstrap? #t result)))
- (append %transformation-options
+ (option '("help-deb-format") #f #f
+ (lambda args
+ (show-deb-format-options/detailed)))
+
+ (append %deb-format-options
+ %transformation-options
%standard-build-options)))
(define (show-help)
@@ -1260,6 +1320,8 @@ Create a bundle of PACKAGE.\n"))
(newline)
(show-transformation-options-help)
(newline)
+ (show-deb-format-options)
+ (newline)
(display (G_ "
-f, --format=FORMAT build a pack in the given FORMAT"))
(display (G_ "
@@ -1369,6 +1431,18 @@ Create a bundle of PACKAGE.\n"))
(else
(packages->manifest packages))))))
+ (define (process-file-arg opts name)
+ ;; Validate that the file exists and return it as a <local-file> object,
+ ;; else #f.
+ (let ((value (assoc-ref opts name)))
+ (match value
+ ((and (? string?) (not (? file-exists?)))
+ (leave (G_ "file provided with option ~a does not exist: ~a~%")
+ (string-append "--" (symbol->string name)) value))
+ ((? string?)
+ (local-file value))
+ (#f #f))))
+
(with-error-handling
(with-store store
(with-status-verbosity (assoc-ref opts 'verbosity)
@@ -1401,6 +1475,15 @@ Create a bundle of PACKAGE.\n"))
manifest)
manifest)))
(pack-format (assoc-ref opts 'format))
+ (extra-options (match pack-format
+ ('deb
+ (list #:control-file
+ (process-file-arg opts 'control-file)
+ #:postinst-file
+ (process-file-arg opts 'postinst-file)
+ #:triggers-file
+ (process-file-arg opts 'triggers-file)))
+ (_ '())))
(target (assoc-ref opts 'target))
(bootstrap? (assoc-ref opts 'bootstrap?))
(compressor (if bootstrap?
@@ -1465,7 +1548,9 @@ to your package list.")))
#:profile-name
profile-name
#:archiver
- archiver)))
+ archiver
+ #:extra-options
+ extra-options)))
(mbegin %store-monad
(mwhen derivation?
(return (format #t "~a~%"
diff --git a/tests/pack.scm b/tests/pack.scm
index 9473d4f384..e9b4c36e0e 100644
--- a/tests/pack.scm
+++ b/tests/pack.scm
@@ -277,17 +277,25 @@
(built-derivations (list check))))
(unless store (test-skip 1))
- (test-assertm "deb archive with symlinks" store
+ (test-assertm "deb archive with symlinks and control files" store
(mlet* %store-monad
((guile (set-guile-for-build (default-guile)))
(profile (profile-derivation (packages->manifest
(list %bootstrap-guile))
#:hooks '()
#:locales? #f))
- (deb (debian-archive "deb-pack" profile
- #:compressor %gzip-compressor
- #:symlinks '(("/opt/gnu/bin" -> "bin"))
- #:archiver %tar-bootstrap))
+ (deb (debian-archive
+ "deb-pack" profile
+ #:compressor %gzip-compressor
+ #:symlinks '(("/opt/gnu/bin" -> "bin"))
+ #:archiver %tar-bootstrap
+ #:extra-options
+ (list #:triggers-file
+ (plain-file "triggers"
+ "activate-noawait /usr/share/icons/hicolor\n")
+ #:postinst-file
+ (plain-file "postinst"
+ "echo running configure script\n"))))
(check
(gexp->derivation "check-deb-pack"
(with-imported-modules '((guix build utils))
@@ -344,6 +352,15 @@
(unless (null? hard-links)
(error "hard links found in data.tar.gz" hard-links))
+ ;; Verify the presence of the control files.
+ (invoke "tar" "-xf" "control.tar.gz")
+ (assert (file-exists? "control"))
+ (assert (and (file-exists? "postinst")
+ (= #o111 ;script is executable
+ (logand #o111 (stat:perms
+ (stat "postinst"))))))
+ (assert (file-exists? "triggers"))
+
(mkdir #$output))))))
(built-derivations (list check)))))