pack: Add '--relocatable'.
* gnu/packages/aux-files/run-in-namespace.c: New file. * Makefile.am (AUX_FILES): Add it. * guix/scripts/pack.scm (<c-compiler>): New record type. (c-compiler, bootstrap-c-compiler, c-compiler-compiler): New procedures. (self-contained-tarball): Use 'relative-file-name' for the SOURCE -> TARGET symlink. (docker-image): Add 'defmod' to please Geiser. (wrapped-package, map-manifest-entries): New procedures. (%options, show-help): Add --relocatable. (guix-pack): Honor it.
This commit is contained in:
parent
54fd5ad0a5
commit
47a60325ca
|
@ -274,7 +274,8 @@ AUX_FILES = \
|
||||||
gnu/packages/aux-files/linux-libre/4.4-i686.conf \
|
gnu/packages/aux-files/linux-libre/4.4-i686.conf \
|
||||||
gnu/packages/aux-files/linux-libre/4.4-x86_64.conf \
|
gnu/packages/aux-files/linux-libre/4.4-x86_64.conf \
|
||||||
gnu/packages/aux-files/linux-libre/4.1-i686.conf \
|
gnu/packages/aux-files/linux-libre/4.1-i686.conf \
|
||||||
gnu/packages/aux-files/linux-libre/4.1-x86_64.conf
|
gnu/packages/aux-files/linux-libre/4.1-x86_64.conf \
|
||||||
|
gnu/packages/aux-files/run-in-namespace.c
|
||||||
|
|
||||||
# Templates, examples.
|
# Templates, examples.
|
||||||
EXAMPLES = \
|
EXAMPLES = \
|
||||||
|
|
|
@ -2834,6 +2834,15 @@ guix pack -S /opt/gnu/bin=bin guile emacs geiser
|
||||||
@noindent
|
@noindent
|
||||||
That way, users can happily type @file{/opt/gnu/bin/guile} and enjoy.
|
That way, users can happily type @file{/opt/gnu/bin/guile} and enjoy.
|
||||||
|
|
||||||
|
@cindex relocatable binaries, with @command{guix pack}
|
||||||
|
What if the recipient of your pack does not have root privileges on
|
||||||
|
their machine, and thus cannot unpack it in the root file system? In
|
||||||
|
that case, you will want to use the @code{--relocatable} option (see
|
||||||
|
below). This option produces @dfn{relocatable binaries}, meaning they
|
||||||
|
they can be placed anywhere in the file system hierarchy: in the example
|
||||||
|
above, users can unpack your tarball in their home directory and
|
||||||
|
directly run @file{./opt/gnu/bin/guile}.
|
||||||
|
|
||||||
Alternatively, you can produce a pack in the Docker image format using
|
Alternatively, you can produce a pack in the Docker image format using
|
||||||
the following command:
|
the following command:
|
||||||
|
|
||||||
|
@ -2867,6 +2876,39 @@ This produces a tarball that follows the
|
||||||
Docker Image Specification}.
|
Docker Image Specification}.
|
||||||
@end table
|
@end table
|
||||||
|
|
||||||
|
@item --relocatable
|
||||||
|
@itemx -R
|
||||||
|
Produce @dfn{relocatable binaries}---i.e., binaries that can be placed
|
||||||
|
anywhere in the file system hierarchy and run from there. For example,
|
||||||
|
if you create a pack containing Bash with:
|
||||||
|
|
||||||
|
@example
|
||||||
|
guix pack -R -S /mybin=bin bash
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
... you can copy that pack to a machine that lacks Guix, and from your
|
||||||
|
home directory as a normal user, run:
|
||||||
|
|
||||||
|
@example
|
||||||
|
tar xf pack.tar.gz
|
||||||
|
./mybin/sh
|
||||||
|
@end example
|
||||||
|
|
||||||
|
@noindent
|
||||||
|
In that shell, if you type @code{ls /gnu/store}, you'll notice that
|
||||||
|
@file{/gnu/store} shows up and contains all the dependencies of
|
||||||
|
@code{bash}, even though the machine actually lacks @file{/gnu/store}
|
||||||
|
altogether! That is probably the simplest way to deploy Guix-built
|
||||||
|
software on a non-Guix machine.
|
||||||
|
|
||||||
|
There's a gotcha though: this technique relies on the @dfn{user
|
||||||
|
namespace} feature of the kernel Linux, which allows unprivileged users
|
||||||
|
to mount or change root. Old versions of Linux did not support it, and
|
||||||
|
some GNU/Linux distributions turn it off; on these systems, programs
|
||||||
|
from the pack @emph{will fail to run}, unless they are unpacked in the
|
||||||
|
root file system.
|
||||||
|
|
||||||
@item --expression=@var{expr}
|
@item --expression=@var{expr}
|
||||||
@itemx -e @var{expr}
|
@itemx -e @var{expr}
|
||||||
Consider the package @var{expr} evaluates to.
|
Consider the package @var{expr} evaluates to.
|
||||||
|
|
|
@ -0,0 +1,264 @@
|
||||||
|
/* GNU Guix --- Functional package management for GNU
|
||||||
|
Copyright (C) 2018 Ludovic Courtès <ludo@gnu.org>
|
||||||
|
|
||||||
|
This file is part of GNU Guix.
|
||||||
|
|
||||||
|
GNU Guix is free software; you can redistribute it and/or modify it
|
||||||
|
under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 3 of the License, or (at
|
||||||
|
your option) any later version.
|
||||||
|
|
||||||
|
GNU Guix is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with GNU Guix. If not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
/* Make the given @WRAPPED_PROGRAM@ relocatable by executing it in a separate
|
||||||
|
mount namespace where the store is mounted in its right place.
|
||||||
|
|
||||||
|
We would happily do that in Scheme using 'call-with-container'. However,
|
||||||
|
this very program needs to be relocatable, so it needs to be statically
|
||||||
|
linked, which complicates things (Guile's modules can hardly be "linked"
|
||||||
|
into a single executable.) */
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
/* Concatenate DIRECTORY, a slash, and FILE. Return the result, which the
|
||||||
|
caller must eventually free. */
|
||||||
|
static char *
|
||||||
|
concat (const char *directory, const char *file)
|
||||||
|
{
|
||||||
|
char *result = malloc (strlen (directory) + 2 + strlen (file));
|
||||||
|
assert (result != NULL);
|
||||||
|
|
||||||
|
strcpy (result, directory);
|
||||||
|
strcat (result, "/");
|
||||||
|
strcat (result, file);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mkdir_p (const char *directory)
|
||||||
|
{
|
||||||
|
if (strcmp (directory, "/") != 0)
|
||||||
|
{
|
||||||
|
char *parent = dirname (strdupa (directory));
|
||||||
|
mkdir_p (parent);
|
||||||
|
int err = mkdir (directory, 0700);
|
||||||
|
if (err < 0 && errno != EEXIST)
|
||||||
|
assert_perror (errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
rm_rf (const char *directory)
|
||||||
|
{
|
||||||
|
DIR *stream = opendir (directory);
|
||||||
|
|
||||||
|
for (struct dirent *entry = readdir (stream);
|
||||||
|
entry != NULL;
|
||||||
|
entry = readdir (stream))
|
||||||
|
{
|
||||||
|
if (strcmp (entry->d_name, ".") == 0
|
||||||
|
|| strcmp (entry->d_name, "..") == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char *full = concat (directory, entry->d_name);
|
||||||
|
|
||||||
|
int err = unlink (full);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
if (errno == EISDIR)
|
||||||
|
/* Recurse (we expect a shallow directory structure so there's
|
||||||
|
little risk of stack overflow.) */
|
||||||
|
rm_rf (full);
|
||||||
|
else
|
||||||
|
assert_perror (errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
free (full);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir (stream);
|
||||||
|
|
||||||
|
int err = rmdir (directory);
|
||||||
|
if (err < 0 && errno != ENOENT)
|
||||||
|
assert_perror (errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bind mount all the top-level entries in SOURCE to TARGET. */
|
||||||
|
static void
|
||||||
|
bind_mount (const char *source, const char *target)
|
||||||
|
{
|
||||||
|
DIR *stream = opendir (source);
|
||||||
|
|
||||||
|
for (struct dirent *entry = readdir (stream);
|
||||||
|
entry != NULL;
|
||||||
|
entry = readdir (stream))
|
||||||
|
{
|
||||||
|
/* XXX: Some file systems may not report a useful 'd_type'. Ignore them
|
||||||
|
for now. */
|
||||||
|
assert (entry->d_type != DT_UNKNOWN);
|
||||||
|
|
||||||
|
if (strcmp (entry->d_name, ".") == 0
|
||||||
|
|| strcmp (entry->d_name, "..") == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char *abs_source = concat (source, entry->d_name);
|
||||||
|
char *new_entry = concat (target, entry->d_name);
|
||||||
|
|
||||||
|
if (entry->d_type == DT_LNK)
|
||||||
|
{
|
||||||
|
char target[PATH_MAX];
|
||||||
|
|
||||||
|
ssize_t result = readlink (abs_source, target, sizeof target - 1);
|
||||||
|
if (result > 0)
|
||||||
|
{
|
||||||
|
target[result] = '\0';
|
||||||
|
int err = symlink (target, new_entry);
|
||||||
|
if (err < 0)
|
||||||
|
assert_perror (errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Create the mount point. */
|
||||||
|
if (entry->d_type == DT_DIR)
|
||||||
|
{
|
||||||
|
int err = mkdir (new_entry, 0700);
|
||||||
|
if (err != 0)
|
||||||
|
assert_perror (errno);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
close (open (new_entry, O_WRONLY | O_CREAT));
|
||||||
|
|
||||||
|
int err = mount (abs_source, new_entry, "none",
|
||||||
|
MS_BIND | MS_REC | MS_RDONLY, NULL);
|
||||||
|
|
||||||
|
/* It used to be that only directories could be bind-mounted. Thus,
|
||||||
|
keep going if we fail to bind-mount a non-directory entry.
|
||||||
|
That's OK because regular files in the root file system are
|
||||||
|
usually uninteresting. */
|
||||||
|
if (err != 0 && entry->d_type != DT_DIR)
|
||||||
|
assert_perror (errno);
|
||||||
|
|
||||||
|
free (new_entry);
|
||||||
|
free (abs_source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir (stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
ssize_t size;
|
||||||
|
char self[PATH_MAX];
|
||||||
|
size = readlink ("/proc/self/exe", self, sizeof self - 1);
|
||||||
|
assert (size > 0);
|
||||||
|
|
||||||
|
/* SELF is something like "/home/ludo/.local/gnu/store/…-foo/bin/ls" and we
|
||||||
|
want to extract "/home/ludo/.local/gnu/store". */
|
||||||
|
size_t index = strlen (self)
|
||||||
|
- strlen ("@WRAPPED_PROGRAM@")
|
||||||
|
+ strlen ("@STORE_DIRECTORY@");
|
||||||
|
char *store = strdup (self);
|
||||||
|
store[index] = '\0';
|
||||||
|
|
||||||
|
struct stat statbuf;
|
||||||
|
|
||||||
|
/* If STORE is already at the "right" place, we can execute
|
||||||
|
@WRAPPED_PROGRAM@ right away. This is not just an optimization: it's
|
||||||
|
needed when running one of these wrappers from within an unshare'd
|
||||||
|
namespace, because 'unshare' fails with EPERM in that context. */
|
||||||
|
if (strcmp (store, "@STORE_DIRECTORY@") != 0
|
||||||
|
&& lstat ("@WRAPPED_PROGRAM@", &statbuf) != 0)
|
||||||
|
{
|
||||||
|
/* Spawn @WRAPPED_PROGRAM@ in a separate namespace where STORE is
|
||||||
|
bind-mounted in the right place. */
|
||||||
|
int err;
|
||||||
|
char *new_root = mkdtemp (strdup ("/tmp/guix-exec-XXXXXX"));
|
||||||
|
char *new_store = concat (new_root, "@STORE_DIRECTORY@");
|
||||||
|
char *cwd = get_current_dir_name ();
|
||||||
|
|
||||||
|
pid_t child = fork ();
|
||||||
|
switch (child)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
/* Unshare namespaces in the child and set up bind-mounts from
|
||||||
|
there. That way, bind-mounts automatically disappear when the
|
||||||
|
child exits, which simplifies cleanup for the parent. */
|
||||||
|
err = unshare (CLONE_NEWNS | CLONE_NEWUSER);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "%s: error: 'unshare' failed: %m\n", argv[0]);
|
||||||
|
fprintf (stderr, "\
|
||||||
|
This may be because \"user namespaces\" are not supported on this system.\n\
|
||||||
|
Consequently, we cannot run '@WRAPPED_PROGRAM@',\n\
|
||||||
|
unless you move it to the '@STORE_DIRECTORY@' directory.\n\
|
||||||
|
\n\
|
||||||
|
Please refer to the 'guix pack' documentation for more information.\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Note: Due to <https://bugzilla.kernel.org/show_bug.cgi?id=183461>
|
||||||
|
we cannot make NEW_ROOT a tmpfs (which would have saved the need
|
||||||
|
for 'rm_rf'.) */
|
||||||
|
bind_mount ("/", new_root);
|
||||||
|
mkdir_p (new_store);
|
||||||
|
err = mount (store, new_store, "none", MS_BIND | MS_REC | MS_RDONLY,
|
||||||
|
NULL);
|
||||||
|
if (err < 0)
|
||||||
|
assert_perror (errno);
|
||||||
|
|
||||||
|
chdir (new_root);
|
||||||
|
err = chroot (new_root);
|
||||||
|
if (err < 0)
|
||||||
|
assert_perror (errno);
|
||||||
|
|
||||||
|
/* Change back to where we were before chroot'ing. */
|
||||||
|
chdir (cwd);
|
||||||
|
break;
|
||||||
|
case -1:
|
||||||
|
assert_perror (errno);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
waitpid (child, &status, 0);
|
||||||
|
chdir ("/"); /* avoid EBUSY */
|
||||||
|
rm_rf (new_root);
|
||||||
|
free (new_root);
|
||||||
|
exit (status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The executable is available under @STORE_DIRECTORY@, so we can now
|
||||||
|
execute it. */
|
||||||
|
int err = execv ("@WRAPPED_PROGRAM@", argv);
|
||||||
|
if (err < 0)
|
||||||
|
assert_perror (errno);
|
||||||
|
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
|
@ -32,6 +32,8 @@
|
||||||
#:use-module (guix packages)
|
#:use-module (guix packages)
|
||||||
#:use-module (guix profiles)
|
#:use-module (guix profiles)
|
||||||
#:use-module (guix derivations)
|
#:use-module (guix derivations)
|
||||||
|
#:use-module (guix search-paths)
|
||||||
|
#:use-module (guix build-system gnu)
|
||||||
#:use-module (guix scripts build)
|
#:use-module (guix scripts build)
|
||||||
#:use-module (gnu packages)
|
#:use-module (gnu packages)
|
||||||
#:use-module (gnu packages bootstrap)
|
#:use-module (gnu packages bootstrap)
|
||||||
|
@ -100,11 +102,14 @@ with a properly initialized store database.
|
||||||
SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
|
SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
|
||||||
added to the pack."
|
added to the pack."
|
||||||
(define build
|
(define build
|
||||||
(with-imported-modules '((guix build utils)
|
(with-imported-modules (source-module-closure
|
||||||
(guix build store-copy)
|
'((guix build utils)
|
||||||
(gnu build install))
|
(guix build union)
|
||||||
|
(guix build store-copy)
|
||||||
|
(gnu build install)))
|
||||||
#~(begin
|
#~(begin
|
||||||
(use-modules (guix build utils)
|
(use-modules (guix build utils)
|
||||||
|
((guix build union) #:select (relative-file-name))
|
||||||
(gnu build install)
|
(gnu build install)
|
||||||
(srfi srfi-1)
|
(srfi srfi-1)
|
||||||
(srfi srfi-26)
|
(srfi srfi-26)
|
||||||
|
@ -119,7 +124,8 @@ added to the pack."
|
||||||
((source '-> target)
|
((source '-> target)
|
||||||
(let ((target (string-append #$profile "/" target)))
|
(let ((target (string-append #$profile "/" target)))
|
||||||
`((directory ,(dirname source))
|
`((directory ,(dirname source))
|
||||||
(,source -> ,target))))))
|
(,source
|
||||||
|
-> ,(relative-file-name (dirname source) target)))))))
|
||||||
|
|
||||||
(define directives
|
(define directives
|
||||||
;; Fully-qualified symlinks.
|
;; Fully-qualified symlinks.
|
||||||
|
@ -217,11 +223,13 @@ the image."
|
||||||
(('gnu rest ...) #t)
|
(('gnu rest ...) #t)
|
||||||
(rest #f)))
|
(rest #f)))
|
||||||
|
|
||||||
|
(define defmod 'define-module) ;trick Geiser
|
||||||
|
|
||||||
(define config
|
(define config
|
||||||
;; (guix config) module for consumption by (guix gcrypt).
|
;; (guix config) module for consumption by (guix gcrypt).
|
||||||
(scheme-file "gcrypt-config.scm"
|
(scheme-file "gcrypt-config.scm"
|
||||||
#~(begin
|
#~(begin
|
||||||
(define-module (guix config)
|
(#$defmod (guix config)
|
||||||
#:export (%libgcrypt))
|
#:export (%libgcrypt))
|
||||||
|
|
||||||
;; XXX: Work around <http://bugs.gnu.org/15602>.
|
;; XXX: Work around <http://bugs.gnu.org/15602>.
|
||||||
|
@ -265,6 +273,150 @@ the image."
|
||||||
build
|
build
|
||||||
#:references-graphs `(("profile" ,profile))))
|
#:references-graphs `(("profile" ,profile))))
|
||||||
|
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Compiling C programs.
|
||||||
|
;;;
|
||||||
|
|
||||||
|
;; A C compiler. That lowers to a single program that can be passed typical C
|
||||||
|
;; compiler flags, and it makes sure the whole toolchain is available.
|
||||||
|
(define-record-type <c-compiler>
|
||||||
|
(%c-compiler toolchain guile)
|
||||||
|
c-compiler?
|
||||||
|
(toolchain c-compiler-toolchain)
|
||||||
|
(guile c-compiler-guile))
|
||||||
|
|
||||||
|
(define* (c-compiler #:optional inputs
|
||||||
|
#:key (guile (default-guile)))
|
||||||
|
(%c-compiler inputs guile))
|
||||||
|
|
||||||
|
(define (bootstrap-c-compiler)
|
||||||
|
"Return the C compiler that uses the bootstrap toolchain. This is used only
|
||||||
|
by '--bootstrap', for testing purposes."
|
||||||
|
(define bootstrap-toolchain
|
||||||
|
(list (first (assoc-ref %bootstrap-inputs "gcc"))
|
||||||
|
(first (assoc-ref %bootstrap-inputs "binutils"))
|
||||||
|
(first (assoc-ref %bootstrap-inputs "libc"))))
|
||||||
|
|
||||||
|
(c-compiler bootstrap-toolchain
|
||||||
|
#:guile %bootstrap-guile))
|
||||||
|
|
||||||
|
(define-gexp-compiler (c-compiler-compiler (compiler <c-compiler>) system target)
|
||||||
|
"Lower COMPILER to a single script that does the right thing."
|
||||||
|
(define toolchain
|
||||||
|
(or (c-compiler-toolchain compiler)
|
||||||
|
(list (first (assoc-ref (standard-packages) "gcc"))
|
||||||
|
(first (assoc-ref (standard-packages) "ld-wrapper"))
|
||||||
|
(first (assoc-ref (standard-packages) "binutils"))
|
||||||
|
(first (assoc-ref (standard-packages) "libc"))
|
||||||
|
(gexp-input (first (assoc-ref (standard-packages) "libc"))
|
||||||
|
"static"))))
|
||||||
|
|
||||||
|
(define inputs
|
||||||
|
(match (append-map package-propagated-inputs
|
||||||
|
(filter package? toolchain))
|
||||||
|
(((labels things . _) ...)
|
||||||
|
(append toolchain things))))
|
||||||
|
|
||||||
|
(define search-paths
|
||||||
|
(cons $PATH
|
||||||
|
(append-map package-native-search-paths
|
||||||
|
(filter package? inputs))))
|
||||||
|
|
||||||
|
(define run
|
||||||
|
(with-imported-modules (source-module-closure
|
||||||
|
'((guix build utils)
|
||||||
|
(guix search-paths)))
|
||||||
|
#~(begin
|
||||||
|
(use-modules (guix build utils) (guix search-paths)
|
||||||
|
(ice-9 match))
|
||||||
|
|
||||||
|
(define (output-file args)
|
||||||
|
(let loop ((args args))
|
||||||
|
(match args
|
||||||
|
(() "a.out")
|
||||||
|
(("-o" file _ ...) file)
|
||||||
|
((head rest ...) (loop rest)))))
|
||||||
|
|
||||||
|
(set-search-paths (map sexp->search-path-specification
|
||||||
|
'#$(map search-path-specification->sexp
|
||||||
|
search-paths))
|
||||||
|
'#$inputs)
|
||||||
|
|
||||||
|
(let ((output (output-file (command-line))))
|
||||||
|
(apply invoke "gcc" (cdr (command-line)))
|
||||||
|
(invoke "strip" output)))))
|
||||||
|
|
||||||
|
(when target
|
||||||
|
;; TODO: Yep, we'll have to do it someday!
|
||||||
|
(leave (G_ "cross-compilation not implemented here;
|
||||||
|
please email '~a'~%")
|
||||||
|
(@ (guix config) %guix-bug-report-address)))
|
||||||
|
|
||||||
|
(gexp->script "c-compiler" run
|
||||||
|
#:guile (c-compiler-guile compiler)))
|
||||||
|
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; Wrapped package.
|
||||||
|
;;;
|
||||||
|
|
||||||
|
(define* (wrapped-package package
|
||||||
|
#:optional (compiler (c-compiler)))
|
||||||
|
(define runner
|
||||||
|
(local-file (search-auxiliary-file "run-in-namespace.c")))
|
||||||
|
|
||||||
|
(define build
|
||||||
|
(with-imported-modules '((guix build utils))
|
||||||
|
#~(begin
|
||||||
|
(use-modules (guix build utils)
|
||||||
|
(ice-9 match))
|
||||||
|
|
||||||
|
(define (strip-store-prefix file)
|
||||||
|
;; Given a file name like "/gnu/store/…-foo-1.2/bin/foo", return
|
||||||
|
;; "/bin/foo".
|
||||||
|
(let* ((len (string-length (%store-directory)))
|
||||||
|
(base (string-drop file (+ 1 len))))
|
||||||
|
(match (string-index base #\/)
|
||||||
|
(#f base)
|
||||||
|
(index (string-drop base index)))))
|
||||||
|
|
||||||
|
(define (build-wrapper program)
|
||||||
|
;; Build a user-namespace wrapper for PROGRAM.
|
||||||
|
(format #t "building wrapper for '~a'...~%" program)
|
||||||
|
(copy-file #$runner "run.c")
|
||||||
|
|
||||||
|
(substitute* "run.c"
|
||||||
|
(("@WRAPPED_PROGRAM@") program)
|
||||||
|
(("@STORE_DIRECTORY@") (%store-directory)))
|
||||||
|
|
||||||
|
(let* ((base (strip-store-prefix program))
|
||||||
|
(result (string-append #$output "/" base)))
|
||||||
|
(mkdir-p (dirname result))
|
||||||
|
(invoke #$compiler "-std=gnu99" "-static" "-Os" "-g0" "-Wall"
|
||||||
|
"run.c" "-o" result)
|
||||||
|
(delete-file "run.c")))
|
||||||
|
|
||||||
|
(setvbuf (current-output-port)
|
||||||
|
(cond-expand (guile-2.2 'line)
|
||||||
|
(else _IOLBF)))
|
||||||
|
(for-each build-wrapper
|
||||||
|
(append (find-files #$(file-append package "/bin"))
|
||||||
|
(find-files #$(file-append package "/sbin"))
|
||||||
|
(find-files #$(file-append package "/libexec")))))))
|
||||||
|
|
||||||
|
(computed-file (string-append (package-full-name package "-") "R")
|
||||||
|
build))
|
||||||
|
|
||||||
|
(define (map-manifest-entries proc manifest)
|
||||||
|
"Apply PROC to all the entries of MANIFEST and return a new manifest."
|
||||||
|
(make-manifest
|
||||||
|
(map (lambda (entry)
|
||||||
|
(manifest-entry
|
||||||
|
(inherit entry)
|
||||||
|
(item (proc (manifest-entry-item entry)))))
|
||||||
|
(manifest-entries manifest))))
|
||||||
|
|
||||||
|
|
||||||
;;;
|
;;;
|
||||||
;;; Command-line options.
|
;;; Command-line options.
|
||||||
|
@ -302,6 +454,9 @@ the image."
|
||||||
(option '(#\f "format") #t #f
|
(option '(#\f "format") #t #f
|
||||||
(lambda (opt name arg result)
|
(lambda (opt name arg result)
|
||||||
(alist-cons 'format (string->symbol arg) result)))
|
(alist-cons 'format (string->symbol arg) result)))
|
||||||
|
(option '(#\R "relocatable") #f #f
|
||||||
|
(lambda (opt name arg result)
|
||||||
|
(alist-cons 'relocatable? #t result)))
|
||||||
(option '(#\e "expression") #t #f
|
(option '(#\e "expression") #t #f
|
||||||
(lambda (opt name arg result)
|
(lambda (opt name arg result)
|
||||||
(alist-cons 'expression arg result)))
|
(alist-cons 'expression arg result)))
|
||||||
|
@ -354,6 +509,8 @@ Create a bundle of PACKAGE.\n"))
|
||||||
(display (G_ "
|
(display (G_ "
|
||||||
-f, --format=FORMAT build a pack in the given FORMAT"))
|
-f, --format=FORMAT build a pack in the given FORMAT"))
|
||||||
(display (G_ "
|
(display (G_ "
|
||||||
|
-R, --relocatable produce relocatable executables"))
|
||||||
|
(display (G_ "
|
||||||
-e, --expression=EXPR consider the package EXPR evaluates to"))
|
-e, --expression=EXPR consider the package EXPR evaluates to"))
|
||||||
(display (G_ "
|
(display (G_ "
|
||||||
-s, --system=SYSTEM attempt to build for SYSTEM--e.g., \"i686-linux\""))
|
-s, --system=SYSTEM attempt to build for SYSTEM--e.g., \"i686-linux\""))
|
||||||
|
@ -417,6 +574,9 @@ Create a bundle of PACKAGE.\n"))
|
||||||
|
|
||||||
(with-error-handling
|
(with-error-handling
|
||||||
(with-store store
|
(with-store store
|
||||||
|
;; Set the build options before we do anything else.
|
||||||
|
(set-build-options-from-command-line store opts)
|
||||||
|
|
||||||
(parameterize ((%graft? (assoc-ref opts 'graft?))
|
(parameterize ((%graft? (assoc-ref opts 'graft?))
|
||||||
(%guile-for-build (package-derivation
|
(%guile-for-build (package-derivation
|
||||||
store
|
store
|
||||||
|
@ -425,7 +585,13 @@ Create a bundle of PACKAGE.\n"))
|
||||||
(canonical-package guile-2.2))
|
(canonical-package guile-2.2))
|
||||||
#:graft? (assoc-ref opts 'graft?))))
|
#:graft? (assoc-ref opts 'graft?))))
|
||||||
(let* ((dry-run? (assoc-ref opts 'dry-run?))
|
(let* ((dry-run? (assoc-ref opts 'dry-run?))
|
||||||
(manifest (manifest-from-args store opts))
|
(relocatable? (assoc-ref opts 'relocatable?))
|
||||||
|
(manifest (let ((manifest (manifest-from-args store opts)))
|
||||||
|
;; Note: We cannot honor '--bootstrap' here because
|
||||||
|
;; 'glibc-bootstrap' lacks 'libc.a'.
|
||||||
|
(if relocatable?
|
||||||
|
(map-manifest-entries wrapped-package manifest)
|
||||||
|
manifest)))
|
||||||
(pack-format (assoc-ref opts 'format))
|
(pack-format (assoc-ref opts 'format))
|
||||||
(name (string-append (symbol->string pack-format)
|
(name (string-append (symbol->string pack-format)
|
||||||
"-pack"))
|
"-pack"))
|
||||||
|
@ -444,12 +610,10 @@ Create a bundle of PACKAGE.\n"))
|
||||||
(leave (G_ "~a: unknown pack format")
|
(leave (G_ "~a: unknown pack format")
|
||||||
format))))
|
format))))
|
||||||
(localstatedir? (assoc-ref opts 'localstatedir?)))
|
(localstatedir? (assoc-ref opts 'localstatedir?)))
|
||||||
;; Set the build options before we do anything else.
|
|
||||||
(set-build-options-from-command-line store opts)
|
|
||||||
|
|
||||||
(run-with-store store
|
(run-with-store store
|
||||||
(mlet* %store-monad ((profile (profile-derivation
|
(mlet* %store-monad ((profile (profile-derivation
|
||||||
manifest
|
manifest
|
||||||
|
#:relative-symlinks? relocatable?
|
||||||
#:hooks (if bootstrap?
|
#:hooks (if bootstrap?
|
||||||
'()
|
'()
|
||||||
%default-profile-hooks)
|
%default-profile-hooks)
|
||||||
|
|
|
@ -20,9 +20,9 @@
|
||||||
# Test the `guix pack' command-line utility.
|
# Test the `guix pack' command-line utility.
|
||||||
#
|
#
|
||||||
|
|
||||||
# A network connection is required to build %bootstrap-coreutils&co,
|
# The bootstrap binaries are needed to run these tests, which usually requires
|
||||||
# which is required to run these tests with the --bootstrap option.
|
# a network connection.
|
||||||
if ! guile -c '(getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)' 2> /dev/null; then
|
if ! guix build -q guile-bootstrap; then
|
||||||
exit 77
|
exit 77
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -87,6 +87,10 @@ guix pack --dry-run --bootstrap -f docker -S /opt/gnu=/ guile-bootstrap
|
||||||
# guile-bootstrap is not intended to be cross-compiled.
|
# guile-bootstrap is not intended to be cross-compiled.
|
||||||
guix pack --dry-run --bootstrap --target=arm-unknown-linux-gnueabihf coreutils
|
guix pack --dry-run --bootstrap --target=arm-unknown-linux-gnueabihf coreutils
|
||||||
|
|
||||||
|
# Likewise, 'guix pack -R' requires a full-blown toolchain (because
|
||||||
|
# 'glibc-bootstrap' lacks 'libc.a'), hence '--dry-run'.
|
||||||
|
guix pack -R --dry-run --bootstrap -S /mybin=bin guile-bootstrap
|
||||||
|
|
||||||
# Make sure package transformation options are honored.
|
# Make sure package transformation options are honored.
|
||||||
mkdir -p "$test_directory"
|
mkdir -p "$test_directory"
|
||||||
drv1="`guix pack -n guile 2>&1 | grep pack.*\.drv`"
|
drv1="`guix pack -n guile 2>&1 | grep pack.*\.drv`"
|
||||||
|
|
Loading…
Reference in New Issue