summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/guix.texi24
-rw-r--r--gnu/build/install.scm1
-rw-r--r--guix/scripts/pack.scm107
3 files changed, 104 insertions, 28 deletions
diff --git a/doc/guix.texi b/doc/guix.texi
index 86fc86da61..82298e677d 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -2422,6 +2422,18 @@ same as would be created by @command{guix package -i}.  It is this
 mechanism that is used to create Guix's own standalone binary tarball
 (@pxref{Binary Installation}).
 
+Users of this pack would have to run
+@file{/gnu/store/@dots{}-profile/bin/guile} to run Guile, which you may
+find inconvenient.  To work around it, you can create, say, a
+@file{/opt/gnu/bin} symlink to the profile:
+
+@example
+guix pack -S /opt/gnu/bin=bin guile emacs geiser
+@end example
+
+@noindent
+That way, users can happily type @file{/opt/gnu/bin/guile} and enjoy.
+
 Several command-line options allow you to customize your pack:
 
 @table @code
@@ -2435,6 +2447,18 @@ the system type of the build host.
 Compress the resulting tarball using @var{tool}---one of @code{gzip},
 @code{bzip2}, @code{xz}, or @code{lzip}.
 
+@item --symlink=@var{spec}
+@itemx -S @var{spec}
+Add the symlinks specified by @var{spec} to the pack.  This option can
+appear several times.
+
+@var{spec} has the form @code{@var{source}=@var{target}}, where
+@var{source} is the symlink that will be created and @var{target} is the
+symlink target.
+
+For instance, @code{-S /opt/gnu/bin=bin} creates a @file{/opt/gnu/bin}
+symlink pointing to the @file{bin} sub-directory of the profile.
+
 @item --localstatedir
 Include the ``local state directory'', @file{/var/guix}, in the
 resulting pack.
diff --git a/gnu/build/install.scm b/gnu/build/install.scm
index 11f107d63c..5cb6055a0c 100644
--- a/gnu/build/install.scm
+++ b/gnu/build/install.scm
@@ -24,6 +24,7 @@
   #:use-module (ice-9 match)
   #:export (install-grub
             install-grub-config
+            evaluate-populate-directive
             populate-root-file-system
             reset-timestamps
             register-closure
diff --git a/guix/scripts/pack.scm b/guix/scripts/pack.scm
index 138e2c5b77..7a0e54d4cd 100644
--- a/guix/scripts/pack.scm
+++ b/guix/scripts/pack.scm
@@ -70,21 +70,41 @@ found."
 (define* (self-contained-tarball name profile
                                  #:key deduplicate?
                                  (compressor (first %compressors))
-                                 localstatedir?)
+                                 localstatedir?
+                                 (symlinks '()))
   "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
-with a properly initialized store database."
+with a properly initialized store database.
+
+SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
+added to the pack."
   (define build
     (with-imported-modules '((guix build utils)
                              (guix build store-copy)
                              (gnu build install))
       #~(begin
           (use-modules (guix build utils)
-                       (gnu build install))
+                       (gnu build install)
+                       (srfi srfi-1)
+                       (srfi srfi-26)
+                       (ice-9 match))
 
           (define %root "root")
 
+          (define symlink->directives
+            ;; Return "populate directives" to make the given symlink and its
+            ;; parent directories.
+            (match-lambda
+              ((source '-> target)
+               (let ((target (string-append #$profile "/" target)))
+                 `((directory ,(dirname source))
+                   (,source -> ,target))))))
+
+          (define directives
+            ;; Fully-qualified symlinks.
+            (append-map symlink->directives '#$symlinks))
+
           ;; We need Guix here for 'guix-register'.
           (setenv "PATH"
                   (string-append #$(if localstatedir?
@@ -102,34 +122,46 @@ with a properly initialized store database."
                                              #:deduplicate? #f
                                              #:register? #$localstatedir?)
 
+          ;; Create SYMLINKS.
+          (for-each (cut evaluate-populate-directive <> %root)
+                    directives)
+
           ;; Create the tarball.  Use GNU format so there's no file name
           ;; length limitation.
           (with-directory-excursion %root
-            (zero? (system* "tar" #$(compressor-tar-option compressor)
-                            "--format=gnu"
-
-                            ;; Avoid non-determinism in the archive.  Use
-                            ;; mtime = 1, not zero, because that is what the
-                            ;; daemon does for files in the store (see the
-                            ;; 'mtimeStore' constant in local-store.cc.)
-                            "--sort=name"
-                            "--mtime=@1"          ;for files in /var/guix
-                            "--owner=root:0"
-                            "--group=root:0"
-
-                            "--check-links"
-                            "-cvf" #$output
-                            ;; Avoid adding / and /var to the tarball, so
-                            ;; that the ownership and permissions of those
-                            ;; directories will not be overwritten when
-                            ;; extracting the archive.  Do not include /root
-                            ;; because the root account might have a
-                            ;; different home directory.
-                            #$@(if localstatedir?
-                                   '("./var/guix")
-                                   '())
-
-                            (string-append "." (%store-directory))))))))
+            (exit
+             (zero? (apply system* "tar" #$(compressor-tar-option compressor)
+                           "--format=gnu"
+
+                           ;; Avoid non-determinism in the archive.  Use
+                           ;; mtime = 1, not zero, because that is what the
+                           ;; daemon does for files in the store (see the
+                           ;; 'mtimeStore' constant in local-store.cc.)
+                           "--sort=name"
+                           "--mtime=@1"           ;for files in /var/guix
+                           "--owner=root:0"
+                           "--group=root:0"
+
+                           "--check-links"
+                           "-cvf" #$output
+                           ;; Avoid adding / and /var to the tarball, so
+                           ;; that the ownership and permissions of those
+                           ;; directories will not be overwritten when
+                           ;; extracting the archive.  Do not include /root
+                           ;; because the root account might have a
+                           ;; different home directory.
+                           #$@(if localstatedir?
+                                  '("./var/guix")
+                                  '())
+
+                           (string-append "." (%store-directory))
+
+                           (delete-duplicates
+                            (filter-map (match-lambda
+                                          (('directory directory)
+                                           (string-append "." directory))
+                                          (_ #f))
+                                        directives)))))))))
 
   (gexp->derivation (string-append name ".tar."
                                    (compressor-extension compressor))
@@ -149,6 +181,7 @@ with a properly initialized store database."
     (graft? . #t)
     (max-silent-time . 3600)
     (verbosity . 0)
+    (symlinks . ())
     (compressor . ,(first %compressors))))
 
 (define %options
@@ -172,6 +205,19 @@ with a properly initialized store database."
                  (lambda (opt name arg result)
                    (alist-cons 'compressor (lookup-compressor arg)
                                result)))
+         (option '(#\S "symlink") #t #f
+                 (lambda (opt name arg result)
+                   (match (string-tokenize arg
+                                           (char-set-complement
+                                            (char-set #\=)))
+                     ((source target)
+                      (let ((symlinks (assoc-ref result 'symlinks)))
+                        (alist-cons 'symlinks
+                                    `((,source -> ,target) ,@symlinks)
+                                    (alist-delete 'symlinks result eq?))))
+                     (x
+                      (leave (_ "~a: invalid symlink specification~%")
+                             arg)))))
          (option '("localstatedir") #f #f
                  (lambda (opt name arg result)
                    (alist-cons 'localstatedir? #t result)))
@@ -191,6 +237,8 @@ Create a bundle of PACKAGE.\n"))
   (display (_ "
   -C, --compression=TOOL compress using TOOL--e.g., \"lzip\""))
   (display (_ "
+  -S, --symlink=SPEC     create symlinks to the profile according to SPEC"))
+  (display (_ "
       --localstatedir    include /var/guix in the resulting pack"))
   (newline)
   (display (_ "
@@ -224,6 +272,7 @@ Create a bundle of PACKAGE.\n"))
                                 list))
                             specs))
              (compressor (assoc-ref opts 'compressor))
+             (symlinks   (assoc-ref opts 'symlinks))
              (localstatedir? (assoc-ref opts 'localstatedir?)))
         (with-store store
           (run-with-store store
@@ -232,6 +281,8 @@ Create a bundle of PACKAGE.\n"))
                                  (drv (self-contained-tarball "pack" profile
                                                               #:compressor
                                                               compressor
+                                                              #:symlinks
+                                                              symlinks
                                                               #:localstatedir?
                                                               localstatedir?)))
               (mbegin %store-monad