From 272c07096251ea3dae237fd016fc5d66fe25e147 Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Sun, 11 Mar 2018 01:13:01 +0100 Subject: tests: Add tests for "guix pack". * guix/scripts/pack.scm (bootstrap-xz): New variable. (%options) <--bootstrap>: New option. (show-help): Document the new --bootstrap option. (guix-pack): When --bootstrap is specified, use the bootstrap Guile, tar, and xz to build the pack, and do not use any profile hooks or locales. * doc/guix.texi (Invoking guix pull): Document the new --bootstrap option. * tests/guix-pack.sh: New file. * Makefile.am (SH_TESTS): Add guix-pack.sh. * gnu/packages/package-management.scm (guix) : Add util-linux. --- guix/scripts/pack.scm | 64 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 17 deletions(-) (limited to 'guix/scripts') diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm index 59dd117ed..0ec1ef4d2 100644 --- a/guix/scripts/pack.scm +++ b/guix/scripts/pack.scm @@ -3,6 +3,7 @@ ;;; Copyright © 2017 Efraim Flashner ;;; Copyright © 2017 Ricardo Wurmus ;;; Copyright © 2018 Konrad Hinsen +;;; Copyright © 2018 Chris Marusich ;;; ;;; This file is part of GNU Guix. ;;; @@ -33,7 +34,9 @@ #:use-module (guix derivations) #:use-module (guix scripts build) #:use-module (gnu packages) + #:use-module (gnu packages bootstrap) #:use-module (gnu packages compression) + #:use-module (gnu packages guile) #:autoload (gnu packages base) (tar) #:autoload (gnu packages package-management) (guix) #:autoload (gnu packages gnupg) (libgcrypt) @@ -67,6 +70,11 @@ #~(#+(file-append bzip2 "/bin/bzip2") "-9")) (compressor "none" "" #f))) +;; This one is only for use in this module, so don't put it in %compressors. +(define bootstrap-xz + (compressor "bootstrap-xz" ".xz" + #~(#+(file-append %bootstrap-coreutils&co "/bin/xz") "-e -T0"))) + (define (lookup-compressor name) "Return the compressor object called NAME. Error out if it could not be found." @@ -325,6 +333,9 @@ the image." (option '("localstatedir") #f #f (lambda (opt name arg result) (alist-cons 'localstatedir? #t result))) + (option '("bootstrap") #f #f + (lambda (opt name arg result) + (alist-cons 'bootstrap? #t result))) (append %transformation-options %standard-build-options))) @@ -352,6 +363,8 @@ Create a bundle of PACKAGE.\n")) -m, --manifest=FILE create a pack with the manifest from FILE")) (display (G_ " --localstatedir include /var/guix in the resulting pack")) + (display (G_ " + --bootstrap use the bootstrap binaries to build the pack")) (newline) (display (G_ " -h, --help display this help and exit")) @@ -393,28 +406,43 @@ Create a bundle of PACKAGE.\n")) (else (packages->manifest packages))))) (with-error-handling - (parameterize ((%graft? (assoc-ref opts 'graft?))) - (let* ((dry-run? (assoc-ref opts 'dry-run?)) - (manifest (manifest-from-args opts)) - (pack-format (assoc-ref opts 'format)) - (name (string-append (symbol->string pack-format) - "-pack")) - (target (assoc-ref opts 'target)) - (compressor (assoc-ref opts 'compressor)) - (symlinks (assoc-ref opts 'symlinks)) - (build-image (match (assq-ref %formats pack-format) - ((? procedure? proc) proc) - (#f - (leave (G_ "~a: unknown pack format") - format)))) - (localstatedir? (assoc-ref opts 'localstatedir?))) - (with-store store + (let* ((dry-run? (assoc-ref opts 'dry-run?)) + (manifest (manifest-from-args opts)) + (pack-format (assoc-ref opts 'format)) + (name (string-append (symbol->string pack-format) + "-pack")) + (target (assoc-ref opts 'target)) + (bootstrap? (assoc-ref opts 'bootstrap?)) + (compressor (if bootstrap? + bootstrap-xz + (assoc-ref opts 'compressor))) + (tar (if bootstrap? + %bootstrap-coreutils&co + tar)) + (symlinks (assoc-ref opts 'symlinks)) + (build-image (match (assq-ref %formats pack-format) + ((? procedure? proc) proc) + (#f + (leave (G_ "~a: unknown pack format") + format)))) + (localstatedir? (assoc-ref opts 'localstatedir?))) + (with-store store + (parameterize ((%graft? (assoc-ref opts 'graft?)) + (%guile-for-build (package-derivation + store + (if (assoc-ref opts 'bootstrap?) + %bootstrap-guile + (canonical-package guile-2.2))))) ;; Set the build options before we do anything else. (set-build-options-from-command-line store opts) (run-with-store store (mlet* %store-monad ((profile (profile-derivation manifest + #:hooks (if bootstrap? + '() + %default-profile-hooks) + #:locales? (not bootstrap?) #:target target)) (drv (build-image name profile #:target @@ -424,7 +452,9 @@ Create a bundle of PACKAGE.\n")) #:symlinks symlinks #:localstatedir? - localstatedir?))) + localstatedir? + #:tar + tar))) (mbegin %store-monad (show-what-to-build* (list drv) #:use-substitutes? -- cgit v1.3 From 1c2ac6b482ea20419e57fd54b0cd1d4d3972776b Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Thu, 15 Mar 2018 05:09:13 +0100 Subject: guix: Rewrite build-docker-image to allow more paths. * guix/docker.scm (build-docker-image): Rename "path" argument to "prefix" to reflect the fact that it is used as a prefix for the symlink targets. Add the "paths" argument, and remove the "closure" argument, since it is now redundant. Add a "transformations" argument. * guix/scripts/pack.scm (docker-image): Read the profile's reference graph and provide its paths to build-docker-image via the new "paths" argument. --- guix/docker.scm | 200 ++++++++++++++++++++++++++++++-------------------- guix/scripts/pack.scm | 9 ++- 2 files changed, 128 insertions(+), 81 deletions(-) (limited to 'guix/scripts') diff --git a/guix/docker.scm b/guix/docker.scm index 060232148..a75534c33 100644 --- a/guix/docker.scm +++ b/guix/docker.scm @@ -1,6 +1,7 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2017 Ricardo Wurmus ;;; Copyright © 2017 Ludovic Courtès +;;; Copyright © 2018 Chris Marusich ;;; ;;; This file is part of GNU Guix. ;;; @@ -23,9 +24,12 @@ #:use-module ((guix build utils) #:select (mkdir-p delete-file-recursively - with-directory-excursion)) - #:use-module (guix build store-copy) + with-directory-excursion + invoke)) #:use-module (srfi srfi-19) + #:use-module (srfi srfi-26) + #:use-module ((texinfo string-utils) + #:select (escape-special-chars)) #:use-module (rnrs bytevectors) #:use-module (ice-9 match) #:export (build-docker-image)) @@ -33,8 +37,7 @@ ;; Load Guile-JSON at run time to simplify the job of 'imported-modules' & co. (module-use! (current-module) (resolve-interface '(json))) -;; Generate a 256-bit identifier in hexadecimal encoding for the Docker image -;; containing the closure at PATH. +;; Generate a 256-bit identifier in hexadecimal encoding for the Docker image. (define docker-id (compose bytevector->base16-string sha256 string->utf8)) @@ -102,82 +105,123 @@ return \"a\"." ((first rest ...) first))) -(define* (build-docker-image image path - #:key closure compressor +(define* (build-docker-image image paths prefix + #:key (symlinks '()) + (transformations '()) (system (utsname:machine (uname))) + compressor (creation-time (current-time time-utc))) - "Write to IMAGE a Docker image archive from the given store PATH. The image -contains the closure of PATH, as specified in CLOSURE (a file produced by -#:references-graphs). SYMLINKS must be a list of (SOURCE -> TARGET) tuples -describing symlinks to be created in the image, where each TARGET is relative -to PATH. SYSTEM is a GNU triplet (or prefix thereof) of the system the -binaries at PATH are for; it is used to produce metadata in the image. - -Use COMPRESSOR, a command such as '(\"gzip\" \"-9n\"), to compress IMAGE. Use -CREATION-TIME, a SRFI-19 time-utc object, as the creation time in metadata." - (let ((directory "/tmp/docker-image") ;temporary working directory - (closure (canonicalize-path closure)) - (id (docker-id path)) - (time (date->string (time-utc->date creation-time) "~4")) - (arch (let-syntax ((cond* (syntax-rules () - ((_ (pattern clause) ...) - (cond ((string-prefix? pattern system) - clause) - ... - (else - (error "unsupported system" - system))))))) - (cond* ("x86_64" "amd64") - ("i686" "386") - ("arm" "arm") - ("mips64" "mips64le"))))) + "Write to IMAGE a Docker image archive containing the given PATHS. PREFIX +must be a store path that is a prefix of any store paths in PATHS. + +SYMLINKS must be a list of (SOURCE -> TARGET) tuples describing symlinks to be +created in the image, where each TARGET is relative to PREFIX. +TRANSFORMATIONS must be a list of (OLD -> NEW) tuples describing how to +transform the PATHS. Any path in PATHS that begins with OLD will be rewritten +in the Docker image so that it begins with NEW instead. If a path is a +non-empty directory, then its contents will be recursively added, as well. + +SYSTEM is a GNU triplet (or prefix thereof) of the system the binaries in +PATHS are for; it is used to produce metadata in the image. Use COMPRESSOR, a +command such as '(\"gzip\" \"-9n\"), to compress IMAGE. Use CREATION-TIME, a +SRFI-19 time-utc object, as the creation time in metadata." + (define (sanitize path-fragment) + (escape-special-chars + ;; GNU tar strips the leading slash off of absolute paths before applying + ;; the transformations, so we need to do the same, or else our + ;; replacements won't match any paths. + (string-trim path-fragment #\/) + ;; Escape the basic regexp special characters (see: "(sed) BRE syntax"). + ;; We also need to escape "/" because we use it as a delimiter. + "/*.^$[]\\" + #\\)) + (define transformation->replacement + (match-lambda + ((old '-> new) + ;; See "(tar) transform" for details on the expression syntax. + (string-append "s/^" (sanitize old) "/" (sanitize new) "/")))) + (define (transformations->expression transformations) + (let ((replacements (map transformation->replacement transformations))) + (string-append + ;; Avoid transforming link targets, since that would break some links + ;; (e.g., symlinks that point to an absolute store path). + "flags=rSH;" + (string-join replacements ";") + ;; Some paths might still have a leading path delimiter even after tar + ;; transforms them (e.g., "/a/b" might be transformed into "/b"), so + ;; strip any leading path delimiters that remain. + ";s,^//*,,"))) + (define transformation-options + (if (eq? '() transformations) + '() + `("--transform" ,(transformations->expression transformations)))) + (let* ((directory "/tmp/docker-image") ;temporary working directory + (id (docker-id prefix)) + (time (date->string (time-utc->date creation-time) "~4")) + (arch (let-syntax ((cond* (syntax-rules () + ((_ (pattern clause) ...) + (cond ((string-prefix? pattern system) + clause) + ... + (else + (error "unsupported system" + system))))))) + (cond* ("x86_64" "amd64") + ("i686" "386") + ("arm" "arm") + ("mips64" "mips64le"))))) ;; Make sure we start with a fresh, empty working directory. (mkdir directory) - - (and (with-directory-excursion directory - (mkdir id) - (with-directory-excursion id - (with-output-to-file "VERSION" - (lambda () (display schema-version))) - (with-output-to-file "json" - (lambda () (scm->json (image-description id time)))) - - ;; Wrap it up. - (let ((items (call-with-input-file closure - read-reference-graph))) - ;; Create SYMLINKS. - (for-each (match-lambda - ((source '-> target) - (let ((source (string-trim source #\/))) - (mkdir-p (dirname source)) - (symlink (string-append path "/" target) - source)))) - symlinks) - - (and (zero? (apply system* "tar" "-cf" "layer.tar" - (append %tar-determinism-options - items - (map symlink-source symlinks)))) - (for-each delete-file-recursively - (map (compose topmost-component symlink-source) - symlinks))))) - - (with-output-to-file "config.json" - (lambda () - (scm->json (config (string-append id "/layer.tar") - time arch)))) - (with-output-to-file "manifest.json" - (lambda () - (scm->json (manifest path id)))) - (with-output-to-file "repositories" - (lambda () - (scm->json (repositories path id))))) - - (and (zero? (apply system* "tar" "-C" directory "-cf" image - `(,@%tar-determinism-options - ,@(if compressor - (list "-I" (string-join compressor)) - '()) - "."))) - (begin (delete-file-recursively directory) #t))))) + (with-directory-excursion directory + (mkdir id) + (with-directory-excursion id + (with-output-to-file "VERSION" + (lambda () (display schema-version))) + (with-output-to-file "json" + (lambda () (scm->json (image-description id time)))) + + ;; Create SYMLINKS. + (for-each (match-lambda + ((source '-> target) + (let ((source (string-trim source #\/))) + (mkdir-p (dirname source)) + (symlink (string-append prefix "/" target) + source)))) + symlinks) + + (apply invoke "tar" "-cf" "layer.tar" + `(,@transformation-options + ,@%tar-determinism-options + ,@paths + ,@(map symlink-source symlinks))) + ;; It is possible for "/" to show up in the archive, especially when + ;; applying transformations. For example, the transformation + ;; "s,^/a,," will (perhaps surprisingly) cause GNU tar to transform + ;; the path "/a" into "/". The presence of "/" in the archive is + ;; probably benign, but it is definitely safe to remove it, so let's + ;; do that. This fails when "/" is not in the archive, so use system* + ;; instead of invoke to avoid an exception in that case. + (system* "tar" "--delete" "/" "-f" "layer.tar") + (for-each delete-file-recursively + (map (compose topmost-component symlink-source) + symlinks))) + + (with-output-to-file "config.json" + (lambda () + (scm->json (config (string-append id "/layer.tar") + time arch)))) + (with-output-to-file "manifest.json" + (lambda () + (scm->json (manifest prefix id)))) + (with-output-to-file "repositories" + (lambda () + (scm->json (repositories prefix id))))) + + (apply invoke "tar" "-cf" image "-C" directory + `(,@%tar-determinism-options + ,@(if compressor + (list "-I" (string-join compressor)) + '()) + ".")) + (delete-file-recursively directory))) diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm index 0ec1ef4d2..488638adc 100644 --- a/guix/scripts/pack.scm +++ b/guix/scripts/pack.scm @@ -238,6 +238,7 @@ the image." (define build (with-imported-modules `(,@(source-module-closure '((guix docker)) #:select? not-config?) + (guix build store-copy) ((guix config) => ,config)) #~(begin ;; Guile-JSON is required by (guix docker). @@ -245,13 +246,15 @@ the image." (string-append #+json "/share/guile/site/" (effective-version))) - (use-modules (guix docker) (srfi srfi-19)) + (use-modules (guix docker) (srfi srfi-19) (guix build store-copy)) (setenv "PATH" (string-append #$tar "/bin")) - (build-docker-image #$output #$profile + (build-docker-image #$output + (call-with-input-file "profile" + read-reference-graph) + #$profile #:system (or #$target (utsname:machine (uname))) - #:closure "profile" #:symlinks '#$symlinks #:compressor '#$(compressor-command compressor) #:creation-time (make-time time-utc 0 1))))) -- cgit v1.3 From a335f6fcc9aac1afb49a562968107abf7c87e631 Mon Sep 17 00:00:00 2001 From: Chris Marusich Date: Mon, 19 Feb 2018 05:45:03 +0100 Subject: system: Add "guix system docker-image" command. * gnu/system/vm.scm (system-docker-image): New procedure. * guix/scripts/system.scm (system-derivation-for-action): Add a case for docker-image, and in that case, call system-docker-image. (show-help): Document docker-image. (guix-system): Parse arguments for docker-image. * doc/guix.texi (Invoking guix system): Document "guix system docker-image". * gnu/system/examples/docker-image.tmpl: New file. --- doc/guix.texi | 36 ++++++++++-- gnu/system/examples/docker-image.tmpl | 47 +++++++++++++++ gnu/system/vm.scm | 105 ++++++++++++++++++++++++++++++++++ guix/scripts/system.scm | 12 ++-- 4 files changed, 192 insertions(+), 8 deletions(-) create mode 100644 gnu/system/examples/docker-image.tmpl (limited to 'guix/scripts') diff --git a/doc/guix.texi b/doc/guix.texi index 9744704ea..a090b2cad 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -20385,12 +20385,18 @@ containing at least the kernel, initrd, and bootloader data files must be created. The @code{--image-size} option can be used to specify the size of the image. +@cindex System images, creation in various formats +@cindex Creating system images in various formats @item vm-image @itemx disk-image -Return a virtual machine or disk image of the operating system declared -in @var{file} that stands alone. By default, @command{guix system} -estimates the size of the image needed to store the system, but you can -use the @option{--image-size} option to specify a value. +@itemx docker-image +Return a virtual machine, disk image, or Docker image of the operating +system declared in @var{file} that stands alone. By default, +@command{guix system} estimates the size of the image needed to store +the system, but you can use the @option{--image-size} option to specify +a value. Docker images are built to contain exactly what they need, so +the @option{--image-size} option is ignored in the case of +@code{docker-image}. You can specify the root file system type by using the @option{--file-system-type} option. It defaults to @code{ext4}. @@ -20408,6 +20414,28 @@ using the following command: # dd if=$(guix system disk-image my-os.scm) of=/dev/sdc @end example +When using @code{docker-image}, a Docker image is produced. Guix builds +the image from scratch, not from a pre-existing Docker base image. As a +result, it contains @emph{exactly} what you define in the operating +system configuration file. You can then load the image and launch a +Docker container using commands like the following: + +@example +image_id="$(docker load < guixsd-docker-image.tar.gz)" +docker run -e GUIX_NEW_SYSTEM=/var/guix/profiles/system \\ + --entrypoint /var/guix/profiles/system/profile/bin/guile \\ + $image_id /var/guix/profiles/system/boot +@end example + +This command starts a new Docker container from the specified image. It +will boot the GuixSD system in the usual manner, which means it will +start any services you have defined in the operating system +configuration. Depending on what you run in the Docker container, it +may be necessary to give the container additional permissions. For +example, if you intend to build software using Guix inside of the Docker +container, you may need to pass the @option{--privileged} option to +@code{docker run}. + @item container Return a script to run the operating system declared in @var{file} within a container. Containers are a set of lightweight isolation diff --git a/gnu/system/examples/docker-image.tmpl b/gnu/system/examples/docker-image.tmpl new file mode 100644 index 000000000..d73187398 --- /dev/null +++ b/gnu/system/examples/docker-image.tmpl @@ -0,0 +1,47 @@ +;; This is an operating system configuration template for a "Docker image" +;; setup, so it has barely any services at all. + +(use-modules (gnu)) + +(operating-system + (host-name "komputilo") + (timezone "Europe/Berlin") + (locale "en_US.utf8") + + ;; This is where user accounts are specified. The "root" account is + ;; implicit, and is initially created with the empty password. + (users (cons (user-account + (name "alice") + (comment "Bob's sister") + (group "users") + (supplementary-groups '("wheel" + "audio" "video")) + (home-directory "/home/alice")) + %base-user-accounts)) + + ;; Globally-installed packages. + (packages %base-packages) + + ;; Because the system will run in a Docker container, we may omit many + ;; things that would normally be required in an operating system + ;; configuration file. These things include: + ;; + ;; * bootloader + ;; * file-systems + ;; * services such as mingetty, udevd, slim, networking, dhcp + ;; + ;; Either these things are simply not required, or Docker provides + ;; similar services for us. + + ;; This will be ignored. + (bootloader (bootloader-configuration + (bootloader grub-bootloader) + (target "does-not-matter"))) + ;; This will be ignored, too. + (file-systems (list (file-system + (device "does-not-matter") + (mount-point "/") + (type "does-not-matter")))) + + ;; Guix is all you need! + (services (list (guix-service)))) diff --git a/gnu/system/vm.scm b/gnu/system/vm.scm index 9d9eafc09..09a11af86 100644 --- a/gnu/system/vm.scm +++ b/gnu/system/vm.scm @@ -23,6 +23,7 @@ (define-module (gnu system vm) #:use-module (guix config) + #:use-module (guix docker) #:use-module (guix store) #:use-module (guix gexp) #:use-module (guix derivations) @@ -30,6 +31,7 @@ #:use-module (guix monads) #:use-module (guix records) #:use-module (guix modules) + #:use-module (guix scripts pack) #:use-module (guix utils) #:use-module (guix hash) #:use-module (guix base32) @@ -39,7 +41,9 @@ #:use-module (gnu packages base) #:use-module (gnu packages bootloaders) #:use-module (gnu packages cdrom) + #:use-module (gnu packages compression) #:use-module (gnu packages guile) + #:autoload (gnu packages gnupg) (libgcrypt) #:use-module (gnu packages gawk) #:use-module (gnu packages bash) #:use-module (gnu packages less) @@ -76,6 +80,7 @@ system-qemu-image/shared-store system-qemu-image/shared-store-script system-disk-image + system-docker-image virtual-machine virtual-machine?)) @@ -377,6 +382,106 @@ the image." #:disk-image-format disk-image-format #:references-graphs inputs)) +(define* (system-docker-image os + #:key + (name "guixsd-docker-image") + register-closures?) + "Build a docker image. OS is the desired . NAME is the +base name to use for the output file. When REGISTER-CLOSURES? is not #f, +register the closure of OS with Guix in the resulting Docker image. This only +makes sense when you want to build a GuixSD Docker image that has Guix +installed inside of it. If you don't need Guix (e.g., your GuixSD Docker +image just contains a web server that is started by the Shepherd), then you +should set REGISTER-CLOSURES? to #f." + (define not-config? + (match-lambda + (('guix 'config) #f) + (('guix rest ...) #t) + (('gnu rest ...) #t) + (rest #f))) + + (define config + ;; (guix config) module for consumption by (guix gcrypt). + (scheme-file "gcrypt-config.scm" + #~(begin + (define-module (guix config) + #:export (%libgcrypt)) + + ;; XXX: Work around . + (eval-when (expand load eval) + (define %libgcrypt + #+(file-append libgcrypt "/lib/libgcrypt")))))) + (mlet %store-monad ((os-drv (operating-system-derivation os #:container? #t)) + (name -> (string-append name ".tar.gz")) + (graph -> "system-graph")) + (define build + (with-imported-modules `(,@(source-module-closure '((guix docker) + (guix build utils) + (gnu build vm)) + #:select? not-config?) + (guix build store-copy) + ((guix config) => ,config)) + #~(begin + ;; Guile-JSON is required by (guix docker). + (add-to-load-path + (string-append #+guile-json "/share/guile/site/" + (effective-version))) + (use-modules (guix docker) + (guix build utils) + (gnu build vm) + (srfi srfi-19) + (guix build store-copy)) + + (let* ((inputs '#$(append (list tar) + (if register-closures? + (list guix) + '()))) + ;; This initializer requires elevated privileges that are + ;; not normally available in the build environment (e.g., + ;; it needs to create device nodes). In order to obtain + ;; such privileges, we run it as root in a VM. + (initialize (root-partition-initializer + #:closures '(#$graph) + #:register-closures? #$register-closures? + #:system-directory #$os-drv + ;; De-duplication would fail due to + ;; cross-device link errors, so don't do it. + #:deduplicate? #f)) + ;; Even as root in a VM, the initializer would fail due to + ;; lack of privileges if we use a root-directory that is on + ;; a file system that is shared with the host (e.g., /tmp). + (root-directory "/guixsd-system-root")) + (set-path-environment-variable "PATH" '("bin" "sbin") inputs) + (mkdir root-directory) + (initialize root-directory) + (build-docker-image + (string-append "/xchg/" #$name) ;; The output file. + (cons* root-directory + (call-with-input-file (string-append "/xchg/" #$graph) + read-reference-graph)) + #$os-drv + #:compressor '(#+(file-append gzip "/bin/gzip") "-9n") + #:creation-time (make-time time-utc 0 1) + #:transformations `((,root-directory -> ""))))))) + (expression->derivation-in-linux-vm + name + ;; The VM's initrd Guile doesn't support dlopen, but our "build" gexp + ;; needs to be run by a Guile that can dlopen libgcrypt. The following + ;; hack works around that problem by putting the "build" gexp into an + ;; executable script (created by program-file) which, when executed, will + ;; run using a Guile that supports dlopen. That way, the VM's initrd + ;; Guile can just execute it via invoke, without using dlopen. See: + ;; https://lists.gnu.org/archive/html/guix-devel/2017-10/msg00233.html + (with-imported-modules `((guix build utils)) + #~(begin + (use-modules (guix build utils)) + ;; If we use execl instead of invoke here, the VM will crash with a + ;; kernel panic. + (invoke #$(program-file "build-docker-image" build)))) + #:make-disk-image? #f + #:single-file-output? #t + #:references-graphs `((,graph ,os-drv))))) + ;;; ;;; VM and disk images. diff --git a/guix/scripts/system.scm b/guix/scripts/system.scm index f0c4a2ba1..b50cabcd1 100644 --- a/guix/scripts/system.scm +++ b/guix/scripts/system.scm @@ -1,7 +1,7 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2014, 2015, 2016, 2017, 2018 Ludovic Courtès ;;; Copyright © 2016 Alex Kost -;;; Copyright © 2016, 2017 Chris Marusich +;;; Copyright © 2016, 2017, 2018 Chris Marusich ;;; Copyright © 2017 Mathieu Othacehe ;;; ;;; This file is part of GNU Guix. @@ -701,7 +701,9 @@ checking this by themselves in their 'check' procedure." ("iso9660" "image.iso") (_ "disk-image")) #:disk-image-size image-size - #:file-system-type file-system-type)))) + #:file-system-type file-system-type)) + ((docker-image) + (system-docker-image os #:register-closures? #t)))) (define (maybe-suggest-running-guix-pull) "Suggest running 'guix pull' if this has never been done before." @@ -904,6 +906,8 @@ Some ACTIONS support additional ARGS.\n")) vm-image build a freestanding virtual machine image\n")) (display (G_ "\ disk-image build a disk image, suitable for a USB stick\n")) + (display (G_ "\ + docker-image build a Docker image\n")) (display (G_ "\ init initialize a root file system to run GNU\n")) (display (G_ "\ @@ -1142,7 +1146,7 @@ argument list and OPTS is the option alist." (case action ((build container vm vm-image disk-image reconfigure init extension-graph shepherd-graph list-generations roll-back - switch-generation search) + switch-generation search docker-image) (alist-cons 'action action result)) (else (leave (G_ "~a: unknown action~%") action)))))) @@ -1171,7 +1175,7 @@ argument list and OPTS is the option alist." (exit 1)) (case action - ((build container vm vm-image disk-image reconfigure) + ((build container vm vm-image disk-image docker-image reconfigure) (unless (or (= count 1) (and expr (= count 0))) (fail))) -- cgit v1.3 From 7d85fcde2343e59bd2eb5ba5d08123877a38da6c Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Mon, 26 Mar 2018 10:05:54 +0200 Subject: guix build: 'guix build --log-file' gracefully reports certificate errors. Previously 'guix build --log-file' would print a backtrace upon X.509 certificate verification errors. * guix/scripts/build.scm (log-url): Catch 'tls-certificate-error' in addition to 'getaddrinfo-error'. --- guix/scripts/build.scm | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'guix/scripts') diff --git a/guix/scripts/build.scm b/guix/scripts/build.scm index 57f2d82c5..401087e83 100644 --- a/guix/scripts/build.scm +++ b/guix/scripts/build.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017 Ludovic Courtès +;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017, 2018 Ludovic Courtès ;;; Copyright © 2013 Mark H Weaver ;;; ;;; This file is part of GNU Guix. @@ -69,13 +69,21 @@ found. Return #f if no build log was found." (define (valid-url? url) ;; Probe URL and return #t if it is accessible. - (catch 'getaddrinfo-error + (catch #t (lambda () (guard (c ((http-get-error? c) #f)) (close-port (http-fetch url #:buffered? #f)) #t)) - (lambda _ - #f))) + (match-lambda* + (('getaddrinfo-error . _) + #f) + (('tls-certificate-error args ...) + (report-error (G_ "cannot access build log at '~a':~%") url) + (print-exception (current-error-port) #f + 'tls-certificate-error args) + (exit 1)) + ((key . args) + (apply throw key args))))) (define (find-url file) (let ((base (basename file))) -- cgit v1.3 From b06a70e05dc6252a3ecb28db5898de7ebc110973 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Tue, 27 Mar 2018 14:00:48 +0200 Subject: graph: Add "module" node type. * guix/scripts/graph.scm (module-from-package) (source-module-dependencies*): New procedures. (%module-node-type): New variable. (%node-types): Add it. * guix/modules.scm (source-module-dependencies): Export. * tests/graph.scm ("module graph"): New test. * doc/guix.texi (Invoking guix graph): Document it. --- doc/guix.texi | 9 +++++++++ guix/modules.scm | 3 ++- guix/scripts/graph.scm | 38 ++++++++++++++++++++++++++++++++++++-- tests/graph.scm | 20 +++++++++++++++++++- 4 files changed, 66 insertions(+), 4 deletions(-) (limited to 'guix/scripts') diff --git a/doc/guix.texi b/doc/guix.texi index 49b3dd10d..220428551 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -6997,6 +6997,15 @@ name instead of a package name, as in: @example guix graph -t derivation `guix system build -d my-config.scm` @end example + +@item module +This is the graph of @dfn{package modules} (@pxref{Package Modules}). +For example, the following command shows the graph for the package +module that defines the @code{guile} package: + +@example +guix graph -t module guile | dot -Tpdf > module-graph.pdf +@end example @end table All the types above correspond to @emph{build-time dependencies}. The diff --git a/guix/modules.scm b/guix/modules.scm index 6c602eda4..bf656bb24 100644 --- a/guix/modules.scm +++ b/guix/modules.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2016, 2017 Ludovic Courtès +;;; Copyright © 2016, 2017, 2018 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -29,6 +29,7 @@ file-name->module-name module-name->file-name + source-module-dependencies source-module-closure live-module-closure guix-module-name?)) diff --git a/guix/scripts/graph.scm b/guix/scripts/graph.scm index 78f09f181..346ca4ea8 100644 --- a/guix/scripts/graph.scm +++ b/guix/scripts/graph.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2015, 2016, 2017 Ludovic Courtès +;;; Copyright © 2015, 2016, 2017, 2018 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -27,9 +27,11 @@ #:use-module (guix gexp) #:use-module (guix derivations) #:use-module (guix memoization) + #:use-module (guix modules) #:use-module ((guix build-system gnu) #:select (standard-packages)) #:use-module (gnu packages) #:use-module (guix sets) + #:use-module ((guix utils) #:select (location-file)) #:use-module (srfi srfi-1) #:use-module (srfi srfi-26) #:use-module (srfi srfi-34) @@ -44,6 +46,7 @@ %derivation-node-type %reference-node-type %referrer-node-type + %module-node-type %node-types guix-graph)) @@ -330,6 +333,36 @@ substitutes." (label store-path-package-name) (edges non-derivation-referrers))) + +;;; +;;; Scheme modules. +;;; + +(define (module-from-package package) + (file-name->module-name (location-file (package-location package)))) + +(define (source-module-dependencies* module) + "Like 'source-module-dependencies' but filter out modules that are not +package modules, while attempting to retain user package modules." + (remove (match-lambda + (('guix _ ...) #t) + (('system _ ...) #t) + (('language _ ...) #t) + (('ice-9 _ ...) #t) + (('srfi _ ...) #t) + (_ #f)) + (source-module-dependencies module))) + +(define %module-node-type + ;; Show the graph of package modules. + (node-type + (name "module") + (description "the graph of package modules") + (convert (lift1 (compose list module-from-package) %store-monad)) + (identifier (lift1 identity %store-monad)) + (label object->string) + (edges (lift1 source-module-dependencies* %store-monad)))) + ;;; ;;; List of node types. @@ -344,7 +377,8 @@ substitutes." %bag-emerged-node-type %derivation-node-type %reference-node-type - %referrer-node-type)) + %referrer-node-type + %module-node-type)) (define (lookup-node-type name) "Return the node type called NAME. Raise an error if it is not found." diff --git a/tests/graph.scm b/tests/graph.scm index 00fd37243..5faa19298 100644 --- a/tests/graph.scm +++ b/tests/graph.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2015, 2016, 2017 Ludovic Courtès +;;; Copyright © 2015, 2016, 2017, 2018 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -271,6 +271,24 @@ edges." (list txt out)) (equal? edges `((,txt ,out))))))))))) +(test-assert "module graph" + (let-values (((backend nodes+edges) (make-recording-backend))) + (run-with-store %store + (export-graph '((gnu packages guile)) 'port + #:node-type %module-node-type + #:backend backend)) + + (let-values (((nodes edges) (nodes+edges))) + (and (member '(gnu packages guile) + (match nodes + (((ids labels) ...) ids))) + (->bool (and (member (list '(gnu packages guile) + '(gnu packages libunistring)) + edges) + (member (list '(gnu packages guile) + '(gnu packages bdw-gc)) + edges))))))) + (test-assert "node-edges" (run-with-store %store (let ((packages (fold-packages cons '()))) -- cgit v1.3 From 8980eea5ab6f89e7649d9abf0be2a9d49156f7d2 Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Tue, 27 Mar 2018 14:16:19 +0200 Subject: guix gc: Add '--derivers'. * guix/scripts/gc.scm (show-help, %options): Add '--derivers'. (guix-gc): Handle 'list-derivers'. * tests/guix-gc.sh: Add test. * doc/guix.texi (Invoking guix gc): Document it. --- doc/guix.texi | 18 ++++++++++++++++++ guix/scripts/gc.scm | 10 +++++++++- tests/guix-gc.sh | 5 ++++- 3 files changed, 31 insertions(+), 2 deletions(-) (limited to 'guix/scripts') diff --git a/doc/guix.texi b/doc/guix.texi index 220428551..c37a87d5a 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -2642,6 +2642,24 @@ of these, recursively. In other words, the returned list is the of an element. @xref{Invoking guix graph}, for a tool to visualize the graph of references. +@item --derivers +@cindex derivation +Return the derivation(s) leading to the given store items +(@pxref{Derivations}). + +For example, this command: + +@example +guix gc --derivers `guix package -I ^emacs$ | cut -f4` +@end example + +@noindent +returns the @file{.drv} file(s) leading to the @code{emacs} package +installed in your profile. + +Note that there may be zero matching @file{.drv} files, for instance +because these files have been garbage-collected. There can also be more +than one matching @file{.drv} due to fixed-output derivations. @end table Lastly, the following options allow you to check the integrity of the diff --git a/guix/scripts/gc.scm b/guix/scripts/gc.scm index a31d2236b..e4ed7227f 100644 --- a/guix/scripts/gc.scm +++ b/guix/scripts/gc.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2012, 2013, 2015, 2016, 2017 Ludovic Courtès +;;; Copyright © 2012, 2013, 2015, 2016, 2017, 2018 Ludovic Courtès ;;; ;;; This file is part of GNU Guix. ;;; @@ -61,6 +61,8 @@ Invoke the garbage collector.\n")) -R, --requisites list the requisites of PATHS")) (display (G_ " --referrers list the referrers of PATHS")) + (display (G_ " + --derivers list the derivers of PATHS")) (newline) (display (G_ " --verify[=OPTS] verify the integrity of the store; OPTS is a @@ -153,6 +155,10 @@ Invoke the garbage collector.\n")) (lambda (opt name arg result) (alist-cons 'action 'list-referrers (alist-delete 'action result)))) + (option '("derivers") #f #f + (lambda (opt name arg result) + (alist-cons 'action 'list-derivers + (alist-delete 'action result)))) (option '("list-failures") #f #f (lambda (opt name arg result) (alist-cons 'action 'list-failures @@ -241,6 +247,8 @@ Invoke the garbage collector.\n")) (requisites store (list item))))) ((list-referrers) (list-relatives referrers)) + ((list-derivers) + (list-relatives valid-derivers)) ((optimize) (assert-no-extra-arguments) (optimize-store store)) diff --git a/tests/guix-gc.sh b/tests/guix-gc.sh index efbc7e759..ef2d9543b 100644 --- a/tests/guix-gc.sh +++ b/tests/guix-gc.sh @@ -1,5 +1,5 @@ # GNU Guix --- Functional package management for GNU -# Copyright © 2013, 2015, 2017 Ludovic Courtès +# Copyright © 2013, 2015, 2017, 2018 Ludovic Courtès # # This file is part of GNU Guix. # @@ -54,6 +54,9 @@ guix gc --references "$out/bin/guile" if guix gc --references /dev/null; then false; else true; fi +# Check derivers. +guix gc --derivers "$out" | grep "$drv" + # Add then reclaim a .drv file. drv="`guix build idutils -d`" test -f "$drv" -- cgit v1.3 From 6fcb90eebd5e21a042ed9746fcbaa2040706ea71 Mon Sep 17 00:00:00 2001 From: Konrad Hinsen Date: Mon, 26 Mar 2018 16:29:24 +0200 Subject: guix environment: load manifest files like "guix package" does MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * guix/scripts/environment.scm (options/resolve-packages): When loading manifest files, use the same module environment as in "guix package". Signed-off-by: Ludovic Courtès --- guix/scripts/environment.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'guix/scripts') diff --git a/guix/scripts/environment.scm b/guix/scripts/environment.scm index 4f88c513c..f8a9702b3 100644 --- a/guix/scripts/environment.scm +++ b/guix/scripts/environment.scm @@ -332,7 +332,7 @@ packages." (let ((module (make-user-module '()))) (packages->outputs (load* file module) mode))) (('manifest . file) - (let ((module (make-user-module '()))) + (let ((module (make-user-module '((guix profiles) (gnu))))) (manifest->outputs (load* file module)))) (_ '(#f))) opts))) -- cgit v1.3 From 183445a6ed1cbac929ecb65303246945c8ccf39d Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Wed, 28 Mar 2018 15:49:11 +0200 Subject: weather: Report continuous integration stats. * guix/scripts/weather.scm (histogram, throughput, queued-subset): New procedures. (report-server-coverage): Report CI information. * doc/guix.texi (Invoking guix weather): Document it. --- doc/guix.texi | 14 +++++- guix/scripts/weather.scm | 109 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 120 insertions(+), 3 deletions(-) (limited to 'guix/scripts') diff --git a/doc/guix.texi b/doc/guix.texi index c37a87d5a..d112b373c 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -7912,15 +7912,27 @@ https://guix.example.org 19,824.2 MiB on disk (uncompressed) 0.030 seconds per request (182.9 seconds in total) 33.5 requests per second + + 9.8% (342 out of 3,470) of the missing items are queued + 867 queued builds + x86_64-linux: 518 (59.7%) + i686-linux: 221 (25.5%) + aarch64-linux: 128 (14.8%) + build rate: 23.41 builds per hour + x86_64-linux: 11.16 builds per hour + i686-linux: 6.03 builds per hour + aarch64-linux: 6.41 builds per hour @end example +@cindex continuous integration, statistics As you can see, it reports the fraction of all the packages for which substitutes are available on the server---regardless of whether substitutes are enabled, and regardless of whether this server's signing key is authorized. It also reports the size of the compressed archives (``nars'') provided by the server, the size the corresponding store items occupy in the store (assuming deduplication is turned off), and -the server's throughput. +the server's throughput. The second part gives continuous integration +(CI) statistics, if the server supports it. To achieve that, @command{guix weather} queries over HTTP(S) meta-data (@dfn{narinfos}) for all the relevant store items. Like @command{guix diff --git a/guix/scripts/weather.scm b/guix/scripts/weather.scm index 2e782e36c..5c934abae 100644 --- a/guix/scripts/weather.scm +++ b/guix/scripts/weather.scm @@ -1,5 +1,5 @@ ;;; GNU Guix --- Functional package management for GNU -;;; Copyright © 2017 Ludovic Courtès +;;; Copyright © 2017, 2018 Ludovic Courtès ;;; Copyright © 2017 Ricardo Wurmus ;;; ;;; This file is part of GNU Guix. @@ -29,11 +29,14 @@ #:use-module (guix grafts) #:use-module ((guix build syscalls) #:select (terminal-columns)) #:use-module (guix scripts substitute) + #:use-module (guix http-client) + #:use-module (guix ci) #:use-module (gnu packages) #:use-module (web uri) #:use-module (srfi srfi-1) #:use-module (srfi srfi-19) #:use-module (srfi srfi-26) + #:use-module (srfi srfi-34) #:use-module (srfi srfi-37) #:use-module (ice-9 match) #:use-module (ice-9 format) @@ -100,6 +103,57 @@ values." (define-syntax-rule (let/time ((time result exp)) body ...) (call-with-time (lambda () exp) (lambda (time result) body ...))) +(define (histogram field proc seed lst) + "Return an alist giving a histogram of all the values of FIELD for elements +of LST. FIELD must be a one element procedure that returns a field's value. +For each FIELD value, call PROC with the previous field-specific result. +Example: + + (histogram car (lambda (x n) (+ 1 n)) 0 '((a . x)(b . y)(a . z))) + => ((a . 2) (b . 1)) + +meaning that we have two a's and one b." + (let loop ((lst lst) + (result '())) + (match lst + (() + result) + ((head . tail) + (let ((value (field head))) + (loop tail + (match (assoc-ref result value) + (#f + `((,value . ,(proc head seed)) ,@result)) + (previous + `((,value . ,(proc head previous)) + ,@(alist-delete value result)))))))))) + +(define (throughput lst timestamp) + "Return the throughput, in items per second, given the elements of LST, +calling TIMESTAMP to get the \"timestamp\" of each item." + (let ((oldest (reduce min +inf.0 (map build-timestamp lst))) + (now (time-second (current-time time-utc)))) + (/ (length lst) (- now oldest) 1.))) + +(define (queued-subset queue items) + "Return the subset of ITEMS, a list of store file names, that appears in +QUEUE, a list of builds. Return #f if elements in QUEUE lack information +about the derivations queued, as is the case with Hydra." + (define queued + (append-map (lambda (build) + (match (false-if-exception + (read-derivation-from-file (build-derivation build))) + (#f + '()) + (drv + (match (derivation->output-paths drv) + (((names . items) ...) items))))) + queue)) + + (if (any (negate build-derivation) queue) + #f ;no derivation information + (lset-intersection string=? queued items))) + (define (report-server-coverage server items) "Report the subset of ITEMS available as substitutes on SERVER." (define MiB (* (expt 2 20) 1.)) @@ -111,6 +165,8 @@ values." (format #t "~a~%" server) (let ((obtained (length narinfos)) (requested (length items)) + (missing (lset-difference string=? + items (map narinfo-path narinfos))) (sizes (filter-map narinfo-file-size narinfos)) (time (+ (time-second time) (/ (time-nanosecond time) 1e9)))) @@ -131,7 +187,56 @@ values." (format #t (G_ " ~,3h seconds per request (~,1h seconds in total)~%") (/ time requested 1.) time) (format #t (G_ " ~,1h requests per second~%") - (/ requested time 1.))))) + (/ requested time 1.)) + + (guard (c ((http-get-error? c) + (if (= 404 (http-get-error-code c)) + (format (current-error-port) + (G_ " (continuous integration information \ +unavailable)~%")) + (format (current-error-port) + (G_ " '~a' returned ~a (~s)~%") + (uri->string (http-get-error-uri c)) + (http-get-error-code c) + (http-get-error-reason c))))) + (let* ((max %query-limit) + (queue (queued-builds server max)) + (len (length queue)) + (histo (histogram build-system + (lambda (build count) + (+ 1 count)) + 0 queue))) + (newline) + (unless (null? missing) + (let ((missing (length missing))) + (match (queued-subset queue missing) + (#f #f) + ((= length queued) + (format #t (G_ " ~,1f% (~h out of ~h) of the missing items \ +are queued~%") + (* 100. (/ queued missing)) + queued missing))))) + + (if (>= len max) + (format #t (G_ " at least ~h queued builds~%") len) + (format #t (G_ " ~h queued builds~%") len)) + (for-each (match-lambda + ((system . count) + (format #t (G_ " ~a: ~a (~0,1f%)~%") + system count (* 100. (/ count len))))) + histo)) + + (let* ((latest (latest-builds server)) + (builds/sec (throughput latest build-timestamp))) + (format #t (G_ " build rate: ~1,2f builds per hour~%") + (* builds/sec 3600.)) + (for-each (match-lambda + ((system . builds) + (format #t (G_ " ~a: ~,2f builds per hour~%") + system + (* (throughput builds build-timestamp) + 3600.)))) + (histogram build-system cons '() latest))))))) ;;; -- cgit v1.3 From fc95dc4c34bf88ebd8c21752bf6d54b5cf752d1a Mon Sep 17 00:00:00 2001 From: Ludovic Courtès Date: Sat, 31 Mar 2018 23:14:56 +0200 Subject: guix package: Add '--allow-collisions'. Fixes . Suggested by Ricardo Wurmus . * guix/scripts/package.scm (build-and-use-profile): Add #:allow-collisions? and pass it to 'profile-derivation'. (show-help, %options): Add '--allow-collisions'. (manifest-action, process-actions): Pass #:allow-collisions? to 'build-and-use-profile'. * tests/guix-package.sh: Add collision test. * doc/guix.texi (Invoking guix package): Document '--allow-collisions'. --- doc/guix.texi | 10 ++++++++++ guix/scripts/package.scm | 17 +++++++++++++++-- tests/guix-package.sh | 8 ++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) (limited to 'guix/scripts') diff --git a/doc/guix.texi b/doc/guix.texi index 25c08b9f0..4eac281a8 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -2039,6 +2039,16 @@ variable, even though, taken individually, neither @file{foo} nor @itemx -p @var{profile} Use @var{profile} instead of the user's default profile. +@cindex collisions, in a profile +@cindex colliding packages in profiles +@cindex profile collisions +@item --allow-collisions +Allow colliding packages in the new profile. Use at your own risk! + +By default, @command{guix package} reports as an error @dfn{collisions} +in the profile. Collisions happen when two or more different versions +or variants of a given package end up in the profile. + @item --verbose Produce verbose output. In particular, emit the build log of the environment on the standard error port. diff --git a/guix/scripts/package.scm b/guix/scripts/package.scm index d8b80efe8..4f519e6f3 100644 --- a/guix/scripts/package.scm +++ b/guix/scripts/package.scm @@ -194,15 +194,18 @@ denote ranges as interpreted by 'matching-generations'." (define* (build-and-use-profile store profile manifest #:key + allow-collisions? bootstrap? use-substitutes? dry-run?) "Build a new generation of PROFILE, a file name, using the packages -specified in MANIFEST, a manifest object." +specified in MANIFEST, a manifest object. When ALLOW-COLLISIONS? is true, +do not treat collisions in MANIFEST as an error." (when (equal? profile %current-profile) (ensure-default-profile)) (let* ((prof-drv (run-with-store store (profile-derivation manifest + #:allow-collisions? allow-collisions? #:hooks (if bootstrap? '() %default-profile-hooks) @@ -407,6 +410,8 @@ Install, remove, or upgrade packages in a single transaction.\n")) (display (G_ " -p, --profile=PROFILE use PROFILE instead of the user's default profile")) (newline) + (display (G_ " + --allow-collisions do not treat collisions in the profile as an error")) (display (G_ " --bootstrap use the bootstrap Guile to build the profile")) (display (G_ " @@ -544,6 +549,10 @@ kind of search path~%") (lambda (opt name arg result arg-handler) (values (alist-cons 'verbose? #t result) #f))) + (option '("allow-collisions") #f #f + (lambda (opt name arg result arg-handler) + (values (alist-cons 'allow-collisions? #t result) + #f))) (option '(#\s "search") #t #f (lambda (opt name arg result arg-handler) (values (cons `(query search ,(or arg "")) @@ -831,13 +840,15 @@ processed, #f otherwise." (let* ((user-module (make-user-module '((guix profiles) (gnu)))) (manifest (load* file user-module)) (bootstrap? (assoc-ref opts 'bootstrap?)) - (substitutes? (assoc-ref opts 'substitutes?))) + (substitutes? (assoc-ref opts 'substitutes?)) + (allow-collisions? (assoc-ref opts 'allow-collisions?))) (if dry-run? (format #t (G_ "would install new manifest from '~a' with ~d entries~%") file (length (manifest-entries manifest))) (format #t (G_ "installing new manifest from '~a' with ~d entries~%") file (length (manifest-entries manifest)))) (build-and-use-profile store profile manifest + #:allow-collisions? allow-collisions? #:bootstrap? bootstrap? #:use-substitutes? substitutes? #:dry-run? dry-run?))) @@ -856,6 +867,7 @@ processed, #f otherwise." (define dry-run? (assoc-ref opts 'dry-run?)) (define bootstrap? (assoc-ref opts 'bootstrap?)) (define substitutes? (assoc-ref opts 'substitutes?)) + (define allow-collisions? (assoc-ref opts 'allow-collisions?)) (define profile (or (assoc-ref opts 'profile) %current-profile)) (define transform (options->transformation opts)) @@ -894,6 +906,7 @@ processed, #f otherwise." (show-manifest-transaction store manifest step3 #:dry-run? dry-run?) (build-and-use-profile store profile new + #:allow-collisions? allow-collisions? #:bootstrap? bootstrap? #:use-substitutes? substitutes? #:dry-run? dry-run?)))) diff --git a/tests/guix-package.sh b/tests/guix-package.sh index 760a2e4c9..aa5eaa66e 100644 --- a/tests/guix-package.sh +++ b/tests/guix-package.sh @@ -60,6 +60,14 @@ test -L "$profile" && test -L "$profile-1-link" ! test -f "$profile-2-link" test -f "$profile/bin/guile" +# Collisions are properly flagged (in this case, 'python-wrapper' propagates +# python@3, which conflicts with python@2.) +if guix package --bootstrap -n -p "$profile" -i python@2 python-wrapper +then false; else true; fi + +guix package --bootstrap -n -p "$profile" -i python@2 python-wrapper \ + --allow-collisions + # No search path env. var. here. guix package -p "$profile" --search-paths guix package -p "$profile" --search-paths | grep '^export PATH=' -- cgit v1.3