From 96915a448cfe8383a1c47f4b9a1cc810e5161fd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludovic=20Court=C3=A8s?= Date: Tue, 27 Nov 2018 15:34:34 +0100 Subject: [PATCH] guix build: Add '--with-branch' transformation option. * guix/scripts/build.scm (evaluate-git-replacement-specs) (transform-package-source-branch): New procedures. (%transformations, %transformation-options): Add 'with-branch'. (show-transformation-options-help): Likewise. * tests/guix-build-branch.sh: New file. * Makefile.am (SH_TESTS): Add it. * doc/guix.texi (Package Transformation Options): Document it. --- Makefile.am | 1 + doc/guix.texi | 27 ++++++++++++++++++ guix/scripts/build.scm | 56 ++++++++++++++++++++++++++++++++++++-- tests/guix-build-branch.sh | 48 ++++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 tests/guix-build-branch.sh diff --git a/Makefile.am b/Makefile.am index 05c450c4f3..e14ac57f2f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -407,6 +407,7 @@ endif SH_TESTS = \ tests/guix-build.sh \ + tests/guix-build-branch.sh \ tests/guix-download.sh \ tests/guix-gc.sh \ tests/guix-hash.sh \ diff --git a/doc/guix.texi b/doc/guix.texi index c040a8531a..491de5c843 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -6451,6 +6451,33 @@ must be compatible. If @var{replacement} is somehow incompatible with @var{package}, then the resulting package may be unusable. Use with care! +@item --with-branch=@var{package}=@var{branch} +@cindex Git, using the latest commit +@cindex latest commit, building +Build @var{package} from the latest commit of @var{branch}. The @code{source} +field of @var{package} must be an origin with the @code{git-fetch} method +(@pxref{origin Reference}) or a @code{git-checkout} object; the repository URL +is taken from that @code{source}. + +For instance, the following command builds @code{guile-sqlite3} from the +latest commit of its @code{master} branch, and then builds @code{guix} (which +depends on it) and @code{cuirass} (which depends on @code{guix}) against this +specific @code{guile-sqlite3} build: + +@example +guix build --with-branch=guile-sqlite3=master cuirass +@end example + +@cindex continuous integration +Obviously, since it uses the latest commit of the given branch, the result of +such a command varies over time. Nevertheless it is a convenient way to +rebuild entire software stacks against the latest commit of one or more +packages. This is particularly useful in the context of continuous +integration (CI). + +Checkouts are kept in a cache under @file{~/.cache/guix/checkouts} to speed up +consecutive accesses to the same repository. You may want to clean it up once +in a while to save disk space. @end table @node Additional Build Options diff --git a/guix/scripts/build.scm b/guix/scripts/build.scm index 13978abb77..e8f2fe973d 100644 --- a/guix/scripts/build.scm +++ b/guix/scripts/build.scm @@ -45,6 +45,8 @@ #:use-module (srfi srfi-37) #:autoload (gnu packages) (specification->package %package-module-path) #:autoload (guix download) (download-to-store) + #:autoload (guix git-download) (git-reference?) + #:autoload (guix git) (git-checkout?) #:use-module (guix status) #:use-module ((guix progress) #:select (current-terminal-columns)) #:use-module ((guix build syscalls) #:select (terminal-columns)) @@ -270,6 +272,48 @@ current 'gnutls' package, after which version 3.5.4 is grafted onto them." (rewrite obj) obj)))) +(define (evaluate-git-replacement-specs specs) + "Parse SPECS, a list of strings like \"guile=stable-2.2\", and return a list +of package pairs. Raise an error if an element of SPECS uses invalid syntax, +or if a package it refers to could not be found." + (define not-equal + (char-set-complement (char-set #\=))) + + (map (lambda (spec) + (match (string-tokenize spec not-equal) + ((name branch) + (let* ((old (specification->package name)) + (source (package-source old)) + (url (cond ((and (origin? source) + (git-reference? (origin-uri source))) + (git-reference-url (origin-uri source))) + ((git-checkout? source) + (git-checkout-url source)) + (else + (leave (G_ "the source of ~a is not a Git \ +reference~%") + (package-full-name old)))))) + (cons old + (package + (inherit old) + (version (string-append "git." branch)) + (source (git-checkout (url url) (branch branch))))))) + (x + (leave (G_ "invalid replacement specification: ~s~%") spec)))) + specs)) + +(define (transform-package-source-branch replacement-specs) + "Return a procedure that, when passed a package, replaces its direct +dependencies according to REPLACEMENT-SPECS. REPLACEMENT-SPECS is a list of +strings like \"guile-next=stable-3.0\" meaning that packages are built using +'guile-next' from the latest commit on its 'stable-3.0' branch." + (let* ((replacements (evaluate-git-replacement-specs replacement-specs)) + (rewrite (package-input-rewriting replacements))) + (lambda (store obj) + (if (package? obj) + (rewrite obj) + obj)))) + (define %transformations ;; Transformations that can be applied to things to build. The car is the ;; key used in the option alist, and the cdr is the transformation @@ -277,7 +321,8 @@ current 'gnutls' package, after which version 3.5.4 is grafted onto them." ;; things to build. `((with-source . ,transform-package-source) (with-input . ,transform-package-inputs) - (with-graft . ,transform-package-inputs/graft))) + (with-graft . ,transform-package-inputs/graft) + (with-branch . ,transform-package-source-branch))) (define %transformation-options ;; The command-line interface to the above transformations. @@ -291,7 +336,9 @@ current 'gnutls' package, after which version 3.5.4 is grafted onto them." (option '("with-input") #t #f (parser 'with-input)) (option '("with-graft") #t #f - (parser 'with-graft))))) + (parser 'with-graft)) + (option '("with-branch") #t #f + (parser 'with-branch))))) (define (show-transformation-options-help) (display (G_ " @@ -302,7 +349,10 @@ current 'gnutls' package, after which version 3.5.4 is grafted onto them." replace dependency PACKAGE by REPLACEMENT")) (display (G_ " --with-graft=PACKAGE=REPLACEMENT - graft REPLACEMENT on packages that refer to PACKAGE"))) + graft REPLACEMENT on packages that refer to PACKAGE")) + (display (G_ " + --with-branch=PACKAGE=BRANCH + build PACKAGE from the latest commit of BRANCH"))) (define (options->transformation opts) diff --git a/tests/guix-build-branch.sh b/tests/guix-build-branch.sh new file mode 100644 index 0000000000..bc50d9c0ef --- /dev/null +++ b/tests/guix-build-branch.sh @@ -0,0 +1,48 @@ +# GNU Guix --- Functional package management for GNU +# Copyright © 2018 Ludovic Courtès +# +# 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 . + +# +# Test 'guix build --with-branch'. +# + +guix build --version + +# 'guix build --with-branch' requires access to the network to clone the +# Git repository below. + +if ! guile -c '(getaddrinfo "www.gnu.org" "80" AI_NUMERICSERV)' 2> /dev/null +then + # Skipping. + exit 77 +fi + +orig_drv="`guix build guile-gcrypt -d`" +latest_drv="`guix build guile-gcrypt --with-branch=guile-gcrypt=master -d`" +test -n "$latest_drv" +test "$orig_drv" != "$latest_drv" + +# FIXME: '-S' currently doesn't work with non-derivation source. +# checkout="`guix build guile-gcrypt --with-branch=guile-gcrypt=master -S`" +checkout="`guix gc --references "$latest_drv" | grep guile-gcrypt | grep -v -E '(-builder|\.drv)'`" +test -d "$checkout" +test -f "$checkout/COPYING" + +orig_drv="`guix build guix -d`" +latest_drv="`guix build guix --with-branch=guile-gcrypt=master -d`" +guix gc -R "$latest_drv" | grep guile-gcrypt-git.master +test "$orig_drv" != "$latest_drv"