aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2014-11-09 22:32:21 +0100
committerLudovic Courtès <ludo@gnu.org>2014-11-09 22:33:45 +0100
commitf81ac34dd9ab0f2ebaabf2cf382bd52d0d78396a (patch)
treef66eec8952dcbd4e09b346ea8132b75af011bd3d
parent4684f301d5a7ff39a913c8f06507f67ec9b4a1cd (diff)
downloadguix-f81ac34dd9ab0f2ebaabf2cf382bd52d0d78396a.tar
guix-f81ac34dd9ab0f2ebaabf2cf382bd52d0d78396a.tar.gz
pull: Use the build procedure provided by the newly-downloaded Guix.
Fixes <http://bugs.gnu.org/18534>. * guix/scripts/pull.scm (with-environment-variable, with-PATH): New macros. (temporary-directory, first-directory, interned-then-deleted): New procedures. (unpack): Rewrite to do the unpacking in the current process rather than as a separate derivation. (%self-build-file): New variable. (build-from-source): New procedure. (build-and-install): Use it. * guix/build/pull.scm (build-guix): Rename 'tarball' argument to 'source'. Remove #:tar and #:gzip parameters, as well as 'tar' invocation. Remove 'scandir' invocation. Wrap body in 'with-directory-excursion'. * build-aux/build-self.scm: New file. * Makefile.am (EXTRA_DIST): Add it.
-rw-r--r--Makefile.am1
-rw-r--r--build-aux/build-self.scm98
-rw-r--r--guix/build/pull.scm120
-rw-r--r--guix/scripts/pull.scm122
4 files changed, 250 insertions, 91 deletions
diff --git a/Makefile.am b/Makefile.am
index b13fcbc053..3350fd6994 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -223,6 +223,7 @@ EXTRA_DIST = \
ROADMAP \
TODO \
.dir-locals.el \
+ build-aux/build-self.scm \
build-aux/hydra/gnu-system.scm \
build-aux/hydra/demo-os.scm \
build-aux/hydra/guix.scm \
diff --git a/build-aux/build-self.scm b/build-aux/build-self.scm
new file mode 100644
index 0000000000..b78f3cb437
--- /dev/null
+++ b/build-aux/build-self.scm
@@ -0,0 +1,98 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2014 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/>.
+
+(define-module (build-self)
+ #:use-module (gnu)
+ #:use-module (guix)
+ #:use-module (srfi srfi-1)
+ #:export (build))
+
+;;; Commentary:
+;;;
+;;; When loaded, this module returns a monadic procedure of at least one
+;;; argument: the source tree to build. It returns a derivation that
+;;; builds it.
+;;;
+;;; This file uses modules provided by the already-installed Guix. Those
+;;; modules may be arbitrarily old compared to the version we want to
+;;; build. Because of that, it must rely on the smallest set of features
+;;; that are likely to be provided by the (guix) and (gnu) modules, and by
+;;; Guile itself, forever and ever.
+;;;
+;;; Code:
+
+
+;; The dependencies. Don't refer explicitly to the variables because they
+;; could be renamed or shuffled around in modules over time. Conversely,
+;; 'find-best-packages-by-name' is expected to always have the same semantics.
+
+(define libgcrypt
+ (first (find-best-packages-by-name "libgcrypt" #f)))
+
+(define guile-json
+ (first (find-best-packages-by-name "guile-json" #f)))
+
+
+
+;; The actual build procedure.
+
+(define (top-source-directory)
+ "Return the name of the top-level directory of this source tree."
+ (and=> (assoc-ref (current-source-location) 'filename)
+ (lambda (file)
+ (string-append (dirname file) "/.."))))
+
+;; The procedure below is our return value.
+(define* (build source #:key verbose?
+ #:allow-other-keys
+ #:rest rest)
+ "Return a derivation that unpacks SOURCE into STORE and compiles Scheme
+files."
+ (define builder
+ #~(begin
+ (use-modules (guix build pull))
+
+ (let ((json (string-append #$guile-json "/share/guile/site/2.0")))
+ (set! %load-path (cons json %load-path))
+ (set! %load-compiled-path (cons json %load-compiled-path)))
+
+ (build-guix #$output #$source
+
+ ;; XXX: This is not perfect, enabling VERBOSE? means
+ ;; building a different derivation.
+ #:debug-port (if #$verbose?
+ (current-error-port)
+ (%make-void-port "w"))
+ #:gcrypt #$libgcrypt)))
+
+ (gexp->derivation "guix-latest" builder
+ #:modules '((guix build pull)
+ (guix build utils))
+
+ ;; Arrange so that our own (guix build …) modules are
+ ;; used.
+ #:module-path (list (top-source-directory))))
+
+;; This file is loaded by 'guix pull'; return it the build procedure.
+build
+
+;; Local Variables:
+;; eval: (put 'with-load-path 'scheme-indent-function 1)
+;; End:
+
+;;; build-self.scm ends here
diff --git a/guix/build/pull.scm b/guix/build/pull.scm
index 841787f0bb..281be23aa8 100644
--- a/guix/build/pull.scm
+++ b/guix/build/pull.scm
@@ -99,76 +99,64 @@ the continuation. Raise an error if one of the processes exit with non-zero."
(lambda ()
(loop lst running completed)))))))))
-(define* (build-guix out tarball
- #:key tar gzip gcrypt
+(define* (build-guix out source
+ #:key gcrypt
(debug-port (%make-void-port "w")))
- "Build and install Guix in directory OUT using source from TARBALL. Write
-any debugging output to DEBUG-PORT."
+ "Build and install Guix in directory OUT using SOURCE, a directory
+containing the source code. Write any debugging output to DEBUG-PORT."
(setvbuf (current-output-port) _IOLBF)
(setvbuf (current-error-port) _IOLBF)
- (setenv "PATH" (string-append tar "/bin:" gzip "/bin"))
-
- (format debug-port "extracting '~a'...~%" tarball)
- (system* "tar" "xf" tarball)
-
- (match (scandir "." (lambda (name)
- (and (not (member name '("." "..")))
- (file-is-directory? name))))
- ((dir)
- (chdir dir))
- (x
- (error "tarball did not produce a single source directory" x)))
-
- (format #t "copying and compiling to '~a'...~%" out)
-
- ;; Copy everything under guix/ and gnu/ plus {guix,gnu}.scm.
- (copy-recursively "guix" (string-append out "/guix")
- #:log debug-port)
- (copy-recursively "gnu" (string-append out "/gnu")
- #:log debug-port)
- (copy-file "guix.scm" (string-append out "/guix.scm"))
- (copy-file "gnu.scm" (string-append out "/gnu.scm"))
-
- ;; Add a fake (guix config) module to allow the other modules to be
- ;; compiled. The user's (guix config) is the one that will be used.
- (copy-file "guix/config.scm.in"
- (string-append out "/guix/config.scm"))
- (substitute* (string-append out "/guix/config.scm")
- (("@LIBGCRYPT@")
- (string-append gcrypt "/lib/libgcrypt")))
-
- ;; Augment the search path so Scheme code can be compiled.
- (set! %load-path (cons out %load-path))
- (set! %load-compiled-path (cons out %load-compiled-path))
-
- ;; Compile the .scm files. Do that in independent processes, à la
- ;; 'make -j', to work around <http://bugs.gnu.org/15602> (FIXME).
- ;; This ensures correctness, but is overly conservative and slow.
- ;; The solution initially implemented (and described in the bug
- ;; above) was slightly faster but consumed memory proportional to the
- ;; number of modules, which quickly became unacceptable.
- (p-for-each (lambda (file)
- (let ((go (string-append (string-drop-right file 4)
- ".go")))
- (format debug-port "~%compiling '~a'...~%" file)
- (parameterize ((current-warning-port debug-port))
- (compile-file file
- #:output-file go
- #:opts
- %auto-compilation-options))))
-
- (filter (cut string-suffix? ".scm" <>)
-
- ;; Build guix/*.scm before gnu/*.scm to speed
- ;; things up.
- (sort (find-files out "\\.scm")
- (let ((guix (string-append out "/guix"))
- (gnu (string-append out "/gnu")))
- (lambda (a b)
- (or (and (string-prefix? guix a)
- (string-prefix? gnu b))
- (string<? a b)))))))
+ (with-directory-excursion source
+ (format #t "copying and compiling to '~a'...~%" out)
+
+ ;; Copy everything under guix/ and gnu/ plus {guix,gnu}.scm.
+ (copy-recursively "guix" (string-append out "/guix")
+ #:log debug-port)
+ (copy-recursively "gnu" (string-append out "/gnu")
+ #:log debug-port)
+ (copy-file "guix.scm" (string-append out "/guix.scm"))
+ (copy-file "gnu.scm" (string-append out "/gnu.scm"))
+
+ ;; Add a fake (guix config) module to allow the other modules to be
+ ;; compiled. The user's (guix config) is the one that will be used.
+ (copy-file "guix/config.scm.in"
+ (string-append out "/guix/config.scm"))
+ (substitute* (string-append out "/guix/config.scm")
+ (("@LIBGCRYPT@")
+ (string-append gcrypt "/lib/libgcrypt")))
+
+ ;; Augment the search path so Scheme code can be compiled.
+ (set! %load-path (cons out %load-path))
+ (set! %load-compiled-path (cons out %load-compiled-path))
+
+ ;; Compile the .scm files. Do that in independent processes, à la
+ ;; 'make -j', to work around <http://bugs.gnu.org/15602> (FIXME).
+ ;; This ensures correctness, but is overly conservative and slow.
+ ;; The solution initially implemented (and described in the bug
+ ;; above) was slightly faster but consumed memory proportional to the
+ ;; number of modules, which quickly became unacceptable.
+ (p-for-each (lambda (file)
+ (let ((go (string-append (string-drop-right file 4)
+ ".go")))
+ (format debug-port "~%compiling '~a'...~%" file)
+ (parameterize ((current-warning-port debug-port))
+ (compile-file file
+ #:output-file go
+ #:opts
+ %auto-compilation-options))))
+
+ (filter (cut string-suffix? ".scm" <>)
+
+ ;; Build guix/*.scm before gnu/*.scm to speed
+ ;; things up.
+ (sort (find-files out "\\.scm")
+ (let ((guix (string-append out "/guix"))
+ (gnu (string-append out "/gnu")))
+ (lambda (a b)
+ (or (and (string-prefix? guix a)
+ (string-prefix? gnu b))
+ (string<? a b))))))))
;; Remove the "fake" (guix config).
(delete-file (string-append out "/guix/config.scm"))
diff --git a/guix/scripts/pull.scm b/guix/scripts/pull.scm
index 919ef2d467..16805bad3f 100644
--- a/guix/scripts/pull.scm
+++ b/guix/scripts/pull.scm
@@ -25,6 +25,8 @@
#:use-module (guix download)
#:use-module (guix gexp)
#:use-module (guix monads)
+ #:use-module ((guix build utils)
+ #:select (with-directory-excursion delete-file-recursively))
#:use-module (gnu packages base)
#:use-module (gnu packages guile)
#:use-module ((gnu packages bootstrap)
@@ -32,7 +34,11 @@
#:use-module (gnu packages compression)
#:use-module (gnu packages gnupg)
#:use-module (srfi srfi-1)
+ #:use-module (srfi srfi-34)
+ #:use-module (srfi srfi-35)
#:use-module (srfi srfi-37)
+ #:use-module (ice-9 ftw)
+ #:use-module (ice-9 match)
#:export (guix-pull))
(define %snapshot-url
@@ -40,31 +46,18 @@
"http://git.savannah.gnu.org/cgit/guix.git/snapshot/guix-master.tar.gz"
)
-(define* (unpack tarball #:key verbose?)
- "Return a derivation that unpacks TARBALL into STORE and compiles Scheme
-files."
- (define builder
- #~(begin
- (use-modules (guix build pull))
+(define-syntax-rule (with-environment-variable variable value body ...)
+ (let ((original (getenv variable)))
+ (dynamic-wind
+ (lambda ()
+ (setenv variable value))
+ (lambda ()
+ body ...)
+ (lambda ()
+ (setenv variable original)))))
- (let ((json (string-append #$guile-json "/share/guile/site/2.0")))
- (set! %load-path (cons json %load-path))
- (set! %load-compiled-path (cons json %load-compiled-path)))
-
- (build-guix #$output #$tarball
-
- ;; XXX: This is not perfect, enabling VERBOSE? means
- ;; building a different derivation.
- #:debug-port (if #$verbose?
- (current-error-port)
- (%make-void-port "w"))
- #:tar #$tar
- #:gzip #$gzip
- #:gcrypt #$libgcrypt)))
-
- (gexp->derivation "guix-latest" builder
- #:modules '((guix build pull)
- (guix build utils))))
+(define-syntax-rule (with-PATH value body ...)
+ (with-environment-variable "PATH" value body ...))
;;;
@@ -118,10 +111,82 @@ Download and deploy the latest version of Guix.\n"))
(define indirect-root-added
(store-lift add-indirect-root))
+(define (temporary-directory)
+ "Make a temporary directory and return its name."
+ (let ((name (tmpnam)))
+ (mkdir name)
+ (chmod name #o700)
+ name))
+
+(define (first-directory directory)
+ "Return a the name of the first file found under DIRECTORY."
+ (match (scandir directory
+ (lambda (name)
+ (and (not (member name '("." "..")))
+ (file-is-directory? name))))
+ ((directory)
+ directory)
+ (x
+ (raise (condition
+ (&message
+ (message "tarball did not produce a single source directory")))))))
+
+(define (interned-then-deleted directory name)
+ "Add DIRECTORY to the store under NAME, and delete it. Return the resulting
+store file name."
+ (mlet %store-monad ((result (interned-file directory name
+ #:recursive? #t)))
+ (delete-file-recursively directory)
+ (return result)))
+
+(define (unpack tarball)
+ "Return the name of the directory where TARBALL has been unpacked."
+ (mlet* %store-monad ((format -> (lift format %store-monad))
+ (tar (package->derivation tar))
+ (gzip (package->derivation gzip)))
+ (mbegin %store-monad
+ (what-to-build (list tar gzip))
+ (built-derivations (list tar gzip))
+ (format #t (_ "unpacking '~a'...~%") tarball)
+
+ (let ((source (temporary-directory)))
+ (with-directory-excursion source
+ (with-PATH (string-append (derivation->output-path gzip) "/bin")
+ (unless (zero? (system* (string-append (derivation->output-path tar)
+ "/bin/tar")
+ "xf" tarball))
+ (raise (condition
+ (&message (message "failed to unpack source code"))))))
+
+ (interned-then-deleted (string-append source "/"
+ (first-directory source))
+ "guix-source"))))))
+
+(define %self-build-file
+ ;; The file containing code to build Guix. This serves the same purpose as
+ ;; a makefile, and, similarly, is intended to always keep this name.
+ "build-aux/build-self.scm")
+
+(define* (build-from-source tarball #:key verbose?)
+ "Return a derivation to build Guix from TARBALL, using the self-build script
+contained therein."
+ ;; Running the self-build script makes it easier to update the build
+ ;; procedure: the self-build script of the Guix-to-be-installed contains the
+ ;; right dependencies, build procedure, etc., which the Guix-in-use may not
+ ;; be know.
+ (mlet* %store-monad ((source (unpack tarball))
+ (script -> (string-append source "/"
+ %self-build-file))
+ (build -> (primitive-load script)))
+ ;; BUILD must be a monadic procedure of at least one argument: the source
+ ;; tree.
+ (build source #:verbose? verbose?)))
+
(define* (build-and-install tarball config-dir
#:key verbose?)
"Build the tool from TARBALL, and install it in CONFIG-DIR."
- (mlet* %store-monad ((source (unpack tarball #:verbose? verbose?))
+ (mlet* %store-monad ((source (build-from-source tarball
+ #:verbose? verbose?))
(source-dir -> (derivation->output-path source))
(to-do? (what-to-build (list source))))
(if to-do?
@@ -165,3 +230,10 @@ Download and deploy the latest version of Guix.\n"))
(run-with-store store
(build-and-install tarball (config-directory)
#:verbose? (assoc-ref opts 'verbose?))))))))
+
+;; Local Variables:
+;; eval: (put 'with-PATH 'scheme-indent-function 1)
+;; eval: (put 'with-temporary-directory 'scheme-indent-function 1)
+;; End:
+
+;;; pull.scm ends here