From 1783c17582906df970c7e68e89d761619a35caeb Mon Sep 17 00:00:00 2001 From: Ricardo Wurmus Date: Sat, 13 Oct 2018 08:39:23 +0200 Subject: [PATCH] guix: Add support for channel dependencies. * guix/channels.scm (%channel-meta-file): New variable. (channel-meta, channel-instance-dependencies): New procedures. (latest-channel-instances): Include channel dependencies. (channel-instance-derivations): Build derivation for additional channels and add it as dependency to the channel instance derivation. * doc/guix.texi (Channels): Add subsection "Declaring Channel Dependencies". --- doc/guix.texi | 33 ++++++++++++++++++++ guix/channels.scm | 79 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 97 insertions(+), 15 deletions(-) diff --git a/doc/guix.texi b/doc/guix.texi index 5ae80917a..a4d5477f6 100644 --- a/doc/guix.texi +++ b/doc/guix.texi @@ -3020,6 +3020,39 @@ the new and upgraded packages that are listed, some like @code{my-gimp} and @code{my-emacs-with-cool-features} might come from @code{my-personal-packages}, while others come from the Guix default channel. +@cindex dependencies, channels +@cindex meta-data, channels +@subsection Declaring Channel Dependencies + +Channel authors may decide to augment a package collection provided by other +channels. They can declare their channel to be dependent on other channels in +a meta-data file @file{.guix-channel}, which is to be placed in the root of +the channel repository. + +The meta-data file should contain a simple S-expression like this: + +@lisp +(channel + (version 0) + (dependencies + (channel + (name 'some-collection) + (url "https://example.org/first-collection.git")) + (channel + (name 'some-other-collection) + (url "https://example.org/second-collection.git") + (branch "testing")))) +@end lisp + +In the above example this channel is declared to depend on two other channels, +which will both be fetched automatically. The modules provided by the channel +will be compiled in an environment where the modules of all these declared +channels are available. + +For the sake of reliability and maintainability, you should avoid dependencies +on channels that you don't control, and you should aim to keep the number of +dependencies to a minimum. + @subsection Replicating Guix @cindex pinning, channels diff --git a/guix/channels.scm b/guix/channels.scm index 82389eb58..fbe1b62bb 100644 --- a/guix/channels.scm +++ b/guix/channels.scm @@ -1,5 +1,6 @@ ;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2018 Ludovic Courtès +;;; Copyright © 2018 Ricardo Wurmus ;;; ;;; This file is part of GNU Guix. ;;; @@ -72,7 +73,6 @@ (commit channel-commit (default #f)) (location channel-location (default (current-source-location)) (innate))) -;; TODO: Add a way to express dependencies among channels. (define %default-channels ;; Default list of channels. @@ -81,6 +81,10 @@ (branch "master") (url "https://git.savannah.gnu.org/git/guix.git")))) +(define %channel-meta-file + ;; The file containing information about the channel. + ".guix-channel") + (define (guix-channel? channel) "Return true if CHANNEL is the 'guix' channel." (eq? 'guix (channel-name channel))) @@ -99,20 +103,52 @@ (#f `(branch . ,(channel-branch channel))) (commit `(commit . ,(channel-commit channel))))) +(define (channel-meta instance) + "Return an S-expression read from the channel INSTANCE's description file, +or return #F if the channel instance does not include the file." + (let* ((source (channel-instance-checkout instance)) + (meta-file (string-append source "/" %channel-meta-file))) + (and (file-exists? meta-file) + (call-with-input-file meta-file read)))) + +(define (channel-instance-dependencies instance) + "Return the list of channels that are declared as dependencies for the given +channel INSTANCE." + (or (and=> (assoc-ref (channel-meta instance) 'dependencies) + (lambda (dependencies) + (map (lambda (item) + (let ((get (lambda* (key #:optional default) + (or (and=> (assoc-ref item key) car) default)))) + (let ((name (get 'name)) + (url (get 'url)) + (branch (get 'branch "master")) + (commit (get 'commit))) + (and name url branch + (channel + (name name) + (branch branch) + (url url) + (commit commit)))))) + dependencies))) + '())) + (define (latest-channel-instances store channels) "Return a list of channel instances corresponding to the latest checkouts of -CHANNELS." - (map (lambda (channel) - (format (current-error-port) - (G_ "Updating channel '~a' from Git repository at '~a'...~%") - (channel-name channel) - (channel-url channel)) - (let-values (((checkout commit) - (latest-repository-commit store (channel-url channel) - #:ref (channel-reference - channel)))) - (channel-instance channel commit checkout))) - channels)) +CHANNELS and the channels on which they depend." + (append-map (lambda (channel) + (format (current-error-port) + (G_ "Updating channel '~a' from Git repository at '~a'...~%") + (channel-name channel) + (channel-url channel)) + (let-values (((checkout commit) + (latest-repository-commit store (channel-url channel) + #:ref (channel-reference + channel)))) + (let ((instance (channel-instance channel commit checkout))) + (cons instance (latest-channel-instances + store + (channel-instance-dependencies instance)))))) + channels)) (define %self-build-file ;; The file containing code to build Guix. This serves the same purpose as @@ -223,8 +259,21 @@ INSTANCES." (lambda (instance) (if (eq? instance core-instance) (return core) - (build-channel-instance instance - (cons core dependencies)))) + (match (channel-instance-dependencies instance) + (() + (build-channel-instance instance + (cons core dependencies))) + (channels + (mlet %store-monad ((dependencies-derivation + (latest-channel-derivation + ;; %default-channels is used here to + ;; ensure that the core channel is + ;; available for channels declared as + ;; dependencies. + (append channels %default-channels)))) + (build-channel-instance instance + (cons dependencies-derivation + (cons core dependencies)))))))) instances))) (define (whole-package-for-legacy name modules) -- 2.19.0