summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/guix.texi13
-rw-r--r--guix/scripts/package.scm44
-rw-r--r--tests/guix-package.sh15
3 files changed, 67 insertions, 5 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index ec784ce349..a07c277e70 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -514,6 +514,19 @@ Thus, when installing MPC, the MPFR and GMP libraries also get installed
 in the profile; removing MPC also removes MPFR and GMP---unless they had
 also been explicitly installed independently.
 
+@item --install-from-expression=@var{exp}
+@itemx -e @var{exp}
+Install the package @var{exp} evaluates to.
+
+@var{exp} must be a Scheme expression that evaluates to a
+@code{<package>} object.  This option is notably useful to disambiguate
+between same-named variants of a package, with expressions such as
+@code{(@@ (gnu packages base) guile-final)}.
+
+Note that this option installs the first output of the specified
+package, which may be insufficient when needing a specific output of a
+multiple-output package.
+
 @item --remove=@var{package}
 @itemx -r @var{package}
 Remove @var{package}.
diff --git a/guix/scripts/package.scm b/guix/scripts/package.scm
index 1f9355ff22..28ef721603 100644
--- a/guix/scripts/package.scm
+++ b/guix/scripts/package.scm
@@ -266,6 +266,26 @@ matching packages."
                        (assoc-ref (derivation-outputs drv) sub-drv))))
          `(,name ,out))))))
 
+(define (read/eval-package-expression str)
+  "Read and evaluate STR and return the package it refers to, or exit an
+error."
+  (let ((exp (catch #t
+               (lambda ()
+                 (call-with-input-string str read))
+               (lambda args
+                 (leave (_ "failed to read expression ~s: ~s~%")
+                        str args)))))
+    (let ((p (catch #t
+               (lambda ()
+                 (eval exp the-scm-module))
+               (lambda args
+                 (leave (_ "failed to evaluate expression `~a': ~s~%")
+                        exp args)))))
+      (if (package? p)
+          p
+          (leave (_ "expression `~s' does not evaluate to a package~%")
+                 exp)))))
+
 
 ;;;
 ;;; Command-line options.
@@ -281,6 +301,9 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n"))
   (display (_ "
   -i, --install=PACKAGE  install PACKAGE"))
   (display (_ "
+  -e, --install-from-expression=EXP
+                         install the package EXP evaluates to"))
+  (display (_ "
   -r, --remove=PACKAGE   remove PACKAGE"))
   (display (_ "
   -u, --upgrade=REGEXP   upgrade all the installed packages matching REGEXP"))
@@ -325,6 +348,10 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n"))
         (option '(#\i "install") #t #f
                 (lambda (opt name arg result)
                   (alist-cons 'install arg result)))
+        (option '(#\e "install-from-expression") #t #f
+                (lambda (opt name arg result)
+                  (alist-cons 'install (read/eval-package-expression arg)
+                              result)))
         (option '(#\r "remove") #t #f
                 (lambda (opt name arg result)
                   (alist-cons 'remove arg result)))
@@ -490,6 +517,19 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n"))
 
       (delete-duplicates (map input->name+path deps) same?))
 
+    (define (package->tuple p)
+      (let ((path (package-derivation (%store) p))
+            (deps (package-transitive-propagated-inputs p)))
+        `(,(package-name p)
+          ,(package-version p)
+
+          ;; When given a package via `-e', install the first of its
+          ;; outputs (XXX).
+          ,(car (package-outputs p))
+
+          ,path
+          ,(canonicalize-deps deps))))
+
     ;; First roll back if asked to.
     (if (and (assoc-ref opts 'roll-back?) (not dry-run?))
         (begin
@@ -515,6 +555,8 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n"))
                (install  (append
                           upgrade
                           (filter-map (match-lambda
+                                       (('install . (? package? p))
+                                        #f)
                                        (('install . (? store-path?))
                                         #f)
                                        (('install . package)
@@ -530,6 +572,8 @@ Install, remove, or upgrade PACKAGES in a single transaction.\n"))
                                      install))
                (install* (append
                           (filter-map (match-lambda
+                                       (('install . (? package? p))
+                                        (package->tuple p))
                                        (('install . (? store-path? path))
                                         (let-values (((name version)
                                                       (package-name->name+version
diff --git a/tests/guix-package.sh b/tests/guix-package.sh
index cf8bc5c7e8..f84893ba0b 100644
--- a/tests/guix-package.sh
+++ b/tests/guix-package.sh
@@ -33,6 +33,10 @@ rm -f "$profile"
 
 trap 'rm "$profile" "$profile-"[0-9]* ; rm -rf t-home-'"$$" EXIT
 
+# Use `-e' with a non-package expression.
+if guix package --bootstrap -e +;
+then false; else true; fi
+
 guix package --bootstrap -p "$profile" -i guile-bootstrap
 test -L "$profile" && test -L "$profile-1-link"
 test -f "$profile/bin/guile"
@@ -46,8 +50,9 @@ test -f "$profile/bin/guile"
 # Check whether we have network access.
 if guile -c '(getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)' 2> /dev/null
 then
-    boot_make="`guix build -e '(@@ (gnu packages base) gnu-make-boot0)'`"
-    guix package --bootstrap -p "$profile" -i "$boot_make"
+    boot_make="(@@ (gnu packages base) gnu-make-boot0)"
+    boot_make_drv="`guix build -e "$boot_make"`"
+    guix package --bootstrap -p "$profile" -i "$boot_make_drv"
     test -L "$profile-2-link"
     test -f "$profile/bin/make" && test -f "$profile/bin/guile"
 
@@ -94,7 +99,7 @@ then
     done
 
     # Reinstall after roll-back to the empty profile.
-    guix package --bootstrap -p "$profile" -i "$boot_make"
+    guix package --bootstrap -p "$profile" -e "$boot_make"
     test "`readlink_base "$profile"`" = "$profile-1-link"
     test -x "$profile/bin/guile" && ! test -x "$profile/bin/make"
 
@@ -104,7 +109,7 @@ then
     test -x "$profile/bin/guile" && ! test -x "$profile/bin/make"
 
     # Install Make.
-    guix package --bootstrap -p "$profile" -i "$boot_make"
+    guix package --bootstrap -p "$profile" -e "$boot_make"
     test "`readlink_base "$profile"`" = "$profile-2-link"
     test -x "$profile/bin/guile" && test -x "$profile/bin/make"
 
@@ -145,7 +150,7 @@ test -f "$HOME/.guix-profile/bin/guile"
 
 if guile -c '(getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)' 2> /dev/null
 then
-    guix package --bootstrap -i "$boot_make"
+    guix package --bootstrap -e "$boot_make"
     test -f "$HOME/.guix-profile/bin/make"
     first_environment="`cd $HOME/.guix-profile ; pwd`"