channels: Strictly check the version of '.guix-channel'.
[jackhill/guix/guix.git] / guix / channels.scm
CommitLineData
0d39a3b9 1;;; GNU Guix --- Functional package management for GNU
49c35bbb 2;;; Copyright © 2018, 2019 Ludovic Courtès <ludo@gnu.org>
af12790b 3;;; Copyright © 2018 Ricardo Wurmus <rekado@elephly.net>
53f21642 4;;; Copyright © 2019 Jan (janneke) Nieuwenhuizen <janneke@gnu.org>
0d39a3b9
LC
5;;;
6;;; This file is part of GNU Guix.
7;;;
8;;; GNU Guix is free software; you can redistribute it and/or modify it
9;;; under the terms of the GNU General Public License as published by
10;;; the Free Software Foundation; either version 3 of the License, or (at
11;;; your option) any later version.
12;;;
13;;; GNU Guix is distributed in the hope that it will be useful, but
14;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;;; GNU General Public License for more details.
17;;;
18;;; You should have received a copy of the GNU General Public License
19;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
20
21(define-module (guix channels)
22 #:use-module (guix git)
23 #:use-module (guix records)
24 #:use-module (guix gexp)
5fbdc9a5 25 #:use-module (guix modules)
0d39a3b9
LC
26 #:use-module (guix discovery)
27 #:use-module (guix monads)
28 #:use-module (guix profiles)
29 #:use-module (guix derivations)
f58f676b 30 #:use-module (guix combinators)
69962ab7 31 #:use-module (guix diagnostics)
0d39a3b9
LC
32 #:use-module (guix store)
33 #:use-module (guix i18n)
ab6025b5
LC
34 #:use-module ((guix utils)
35 #:select (source-properties->location
36 &error-location))
0d39a3b9 37 #:use-module (srfi srfi-1)
af12790b 38 #:use-module (srfi srfi-2)
0d39a3b9
LC
39 #:use-module (srfi srfi-9)
40 #:use-module (srfi srfi-11)
ab6025b5
LC
41 #:use-module (srfi srfi-34)
42 #:use-module (srfi srfi-35)
5fbdc9a5
LC
43 #:autoload (guix self) (whole-package make-config.scm)
44 #:autoload (guix inferior) (gexp->derivation-in-inferior) ;FIXME: circular dep
0d39a3b9 45 #:use-module (ice-9 match)
ed75bdf3 46 #:use-module (ice-9 vlist)
0d39a3b9
LC
47 #:export (channel
48 channel?
49 channel-name
50 channel-url
51 channel-branch
52 channel-commit
53 channel-location
54
55 %default-channels
72f749dc 56 guix-channel?
0d39a3b9
LC
57
58 channel-instance?
59 channel-instance-channel
60 channel-instance-commit
61 channel-instance-checkout
62
63 latest-channel-instances
fe5db4eb 64 checkout->channel-instance
030f1367 65 latest-channel-derivation
c37f38bd 66 channel-instances->manifest
5fbdc9a5 67 %channel-profile-hooks
c37f38bd 68 channel-instances->derivation))
0d39a3b9
LC
69
70;;; Commentary:
71;;;
72;;; This module implements "channels." A channel is usually a source of
73;;; package definitions. There's a special channel, the 'guix' channel, that
74;;; provides all of Guix, including its commands and its documentation.
75;;; User-defined channels are expected to typically provide a bunch of .scm
76;;; files meant to be added to the '%package-search-path'.
77;;;
78;;; This module provides tools to fetch and update channels from a Git
79;;; repository and to build them.
80;;;
81;;; Code:
82
83(define-record-type* <channel> channel make-channel
84 channel?
85 (name channel-name)
86 (url channel-url)
87 (branch channel-branch (default "master"))
88 (commit channel-commit (default #f))
89 (location channel-location
90 (default (current-source-location)) (innate)))
0d39a3b9
LC
91
92(define %default-channels
93 ;; Default list of channels.
94 (list (channel
95 (name 'guix)
37a6cdbf 96 (branch "master")
0d39a3b9
LC
97 (url "https://git.savannah.gnu.org/git/guix.git"))))
98
99(define (guix-channel? channel)
100 "Return true if CHANNEL is the 'guix' channel."
101 (eq? 'guix (channel-name channel)))
102
103(define-record-type <channel-instance>
104 (channel-instance channel commit checkout)
105 channel-instance?
106 (channel channel-instance-channel)
107 (commit channel-instance-commit)
108 (checkout channel-instance-checkout))
109
af12790b 110(define-record-type <channel-metadata>
53f21642 111 (channel-metadata version directory dependencies)
af12790b
RW
112 channel-metadata?
113 (version channel-metadata-version)
53f21642 114 (directory channel-metadata-directory)
af12790b
RW
115 (dependencies channel-metadata-dependencies))
116
0d39a3b9
LC
117(define (channel-reference channel)
118 "Return the \"reference\" for CHANNEL, an sexp suitable for
119'latest-repository-commit'."
120 (match (channel-commit channel)
121 (#f `(branch . ,(channel-branch channel)))
122 (commit `(commit . ,(channel-commit channel)))))
123
45b90332
LC
124(define (read-channel-metadata port)
125 "Read from PORT channel metadata in the format expected for the
126'.guix-channel' file. Return a <channel-metadata> record, or raise an error
127if valid metadata could not be read from PORT."
128 (match (read port)
129 (('channel ('version 0) properties ...)
130 (let ((directory (and=> (assoc-ref properties 'directory) first))
131 (dependencies (or (assoc-ref properties 'dependencies) '())))
132 (channel-metadata
133 version
134 directory
135 (map (lambda (item)
136 (let ((get (lambda* (key #:optional default)
137 (or (and=> (assoc-ref item key) first) default))))
138 (and-let* ((name (get 'name))
139 (url (get 'url))
140 (branch (get 'branch "master")))
141 (channel
142 (name name)
143 (branch branch)
144 (url url)
145 (commit (get 'commit))))))
146 dependencies))))
147 ((and ('channel ('version version) _ ...) sexp)
148 (raise (condition
149 (&message (message "unsupported '.guix-channel' version"))
150 (&error-location
151 (location (source-properties->location
152 (source-properties sexp)))))))
153 (sexp
154 (raise (condition
155 (&message (message "invalid '.guix-channel' file"))
156 (&error-location
157 (location (source-properties->location
158 (source-properties sexp)))))))))
159
53f21642
JN
160(define (read-channel-metadata-from-source source)
161 "Return a channel-metadata record read from channel's SOURCE/.guix-channel
162description file, or return #F if SOURCE/.guix-channel does not exist."
45b90332
LC
163 (catch 'system-error
164 (lambda ()
165 (call-with-input-file (string-append source "/.guix-channel")
166 read-channel-metadata))
167 (lambda args
168 (if (= ENOENT (system-error-errno args))
169 #f
170 (apply throw args)))))
171
172(define (channel-instance-metadata instance)
53f21642
JN
173 "Return a channel-metadata record read from the channel INSTANCE's
174description file, or return #F if the channel instance does not include the
175file."
176 (read-channel-metadata-from-source (channel-instance-checkout instance)))
177
af12790b
RW
178(define (channel-instance-dependencies instance)
179 "Return the list of channels that are declared as dependencies for the given
180channel INSTANCE."
45b90332 181 (match (channel-instance-metadata instance)
af12790b 182 (#f '())
53f21642 183 (($ <channel-metadata> version directory dependencies)
af12790b
RW
184 dependencies)))
185
186(define* (latest-channel-instances store channels #:optional (previous-channels '()))
0d39a3b9 187 "Return a list of channel instances corresponding to the latest checkouts of
af12790b
RW
188CHANNELS and the channels on which they depend. PREVIOUS-CHANNELS is a list
189of previously processed channels."
190 ;; Only process channels that are unique, or that are more specific than a
191 ;; previous channel specification.
192 (define (ignore? channel others)
193 (member channel others
194 (lambda (a b)
195 (and (eq? (channel-name a) (channel-name b))
196 (or (channel-commit b)
197 (not (or (channel-commit a)
198 (channel-commit b))))))))
f58f676b 199
af12790b
RW
200 ;; Accumulate a list of instances. A list of processed channels is also
201 ;; accumulated to decide on duplicate channel specifications.
f58f676b
LC
202 (define-values (resulting-channels instances)
203 (fold2 (lambda (channel previous-channels instances)
204 (if (ignore? channel previous-channels)
205 (values previous-channels instances)
206 (begin
207 (format (current-error-port)
208 (G_ "Updating channel '~a' from Git repository at '~a'...~%")
209 (channel-name channel)
210 (channel-url channel))
211 (let-values (((checkout commit)
212 (latest-repository-commit store (channel-url channel)
213 #:ref (channel-reference
214 channel))))
215 (let ((instance (channel-instance channel commit checkout)))
216 (let-values (((new-instances new-channels)
217 (latest-channel-instances
218 store
219 (channel-instance-dependencies instance)
220 previous-channels)))
221 (values (append (cons channel new-channels)
222 previous-channels)
223 (append (cons instance new-instances)
224 instances))))))))
225 previous-channels
226 '() ;instances
227 channels))
228
229 (let ((instance-name (compose channel-name channel-instance-channel)))
230 ;; Remove all earlier channel specifications if they are followed by a
231 ;; more specific one.
232 (values (delete-duplicates instances
233 (lambda (a b)
234 (eq? (instance-name a) (instance-name b))))
235 resulting-channels)))
0d39a3b9 236
fe5db4eb
LC
237(define* (checkout->channel-instance checkout
238 #:key commit
239 (url checkout) (name 'guix))
240 "Return a channel instance for CHECKOUT, which is assumed to be a checkout
241of COMMIT at URL. Use NAME as the channel name."
242 (let* ((commit (or commit (make-string 40 #\0)))
243 (channel (channel (name name)
244 (commit commit)
245 (url url))))
246 (channel-instance channel commit checkout)))
247
0d39a3b9
LC
248(define %self-build-file
249 ;; The file containing code to build Guix. This serves the same purpose as
250 ;; a makefile, and, similarly, is intended to always keep this name.
251 "build-aux/build-self.scm")
252
253(define %pull-version
254 ;; This is the version of the 'guix pull' protocol. It specifies what's
255 ;; expected from %SELF-BUILD-FILE. The initial version ("0") was when we'd
256 ;; place a set of compiled Guile modules in ~/.config/guix/latest.
257 1)
258
acefa740
LC
259(define (standard-module-derivation name source core dependencies)
260 "Return a derivation that builds with CORE, a Guix instance, the Scheme
261modules in SOURCE and that depend on DEPENDENCIES, a list of lowerable
262objects. The assumption is that SOURCE contains package modules to be added
263to '%package-module-path'."
acefa740 264
53f21642
JN
265 (let* ((metadata (read-channel-metadata-from-source source))
266 (directory (and=> metadata channel-metadata-directory)))
267
268 (define build
269 ;; This is code that we'll run in CORE, a Guix instance, with its own
270 ;; modules and so on. That way, we make sure these modules are built for
271 ;; the right Guile version, with the right dependencies, and that they get
272 ;; to see the right (gnu packages …) modules.
273 (with-extensions dependencies
274 #~(begin
275 (use-modules (guix build compile)
276 (guix build utils)
277 (srfi srfi-26))
278
279 (define go
280 (string-append #$output "/lib/guile/" (effective-version)
281 "/site-ccache"))
282 (define scm
283 (string-append #$output "/share/guile/site/"
284 (effective-version)))
acefa740 285
53f21642
JN
286 (let* ((subdir (if #$directory
287 (string-append "/" #$directory)
288 ""))
289 (source (string-append #$source subdir)))
290 (compile-files source go (find-files source "\\.scm$"))
291 (mkdir-p (dirname scm))
292 (symlink (string-append #$source subdir) scm))
acefa740 293
53f21642 294 scm)))
acefa740 295
53f21642 296 (gexp->derivation-in-inferior name build core)))
0d39a3b9
LC
297
298(define* (build-from-source name source
acefa740 299 #:key core verbose? commit
0d39a3b9
LC
300 (dependencies '()))
301 "Return a derivation to build Guix from SOURCE, using the self-build script
acefa740
LC
302contained therein; use COMMIT as the version string. When CORE is true, build
303package modules under SOURCE using CORE, an instance of Guix."
0d39a3b9
LC
304 ;; Running the self-build script makes it easier to update the build
305 ;; procedure: the self-build script of the Guix-to-be-installed contains the
306 ;; right dependencies, build procedure, etc., which the Guix-in-use may not
307 ;; be know.
308 (define script
309 (string-append source "/" %self-build-file))
310
311 (if (file-exists? script)
312 (let ((build (save-module-excursion
313 (lambda ()
3a8c4860
LC
314 ;; Disable deprecation warnings; it's OK for SCRIPT to
315 ;; use deprecated APIs and the user doesn't have to know
316 ;; about it.
69962ab7 317 (parameterize ((guix-warning-port
3a8c4860
LC
318 (%make-void-port "w")))
319 (primitive-load script))))))
0d39a3b9
LC
320 ;; BUILD must be a monadic procedure of at least one argument: the
321 ;; source tree.
322 ;;
323 ;; Note: BUILD can return #f if it does not support %PULL-VERSION. In
324 ;; the future we'll fall back to a previous version of the protocol
325 ;; when that happens.
326 (build source #:verbose? verbose? #:version commit
327 #:pull-version %pull-version))
328
329 ;; Build a set of modules that extend Guix using the standard method.
acefa740 330 (standard-module-derivation name source core dependencies)))
0d39a3b9 331
acefa740
LC
332(define* (build-channel-instance instance
333 #:optional core (dependencies '()))
0d39a3b9
LC
334 "Return, as a monadic value, the derivation for INSTANCE, a channel
335instance. DEPENDENCIES is a list of extensions providing Guile modules that
336INSTANCE depends on."
337 (build-from-source (symbol->string
338 (channel-name (channel-instance-channel instance)))
339 (channel-instance-checkout instance)
340 #:commit (channel-instance-commit instance)
acefa740 341 #:core core
0d39a3b9
LC
342 #:dependencies dependencies))
343
ed75bdf3
LC
344(define (resolve-dependencies instances)
345 "Return a procedure that, given one of the elements of INSTANCES, returns
346list of instances it depends on."
347 (define channel-instance-name
348 (compose channel-name channel-instance-channel))
349
350 (define table ;map a name to an instance
351 (fold (lambda (instance table)
352 (vhash-consq (channel-instance-name instance)
353 instance table))
354 vlist-null
355 instances))
356
357 (define edges
358 (fold (lambda (instance edges)
359 (fold (lambda (channel edges)
360 (let ((name (channel-name channel)))
361 (match (vhash-assq name table)
362 ((_ . target)
363 (vhash-consq instance target edges)))))
364 edges
365 (channel-instance-dependencies instance)))
366 vlist-null
367 instances))
368
369 (lambda (instance)
370 (vhash-foldq* cons '() instance edges)))
371
0d39a3b9
LC
372(define (channel-instance-derivations instances)
373 "Return the list of derivations to build INSTANCES, in the same order as
374INSTANCES."
375 (define core-instance
376 ;; The 'guix' channel is treated specially: it's an implicit dependency of
377 ;; all the other channels.
378 (find (lambda (instance)
379 (guix-channel? (channel-instance-channel instance)))
380 instances))
381
ed75bdf3
LC
382 (define edges
383 (resolve-dependencies instances))
384
385 (define (instance->derivation instance)
cdf68947
LC
386 (mlet %store-monad ((system (current-system)))
387 (mcached (if (eq? instance core-instance)
388 (build-channel-instance instance)
389 (mlet %store-monad ((core (instance->derivation core-instance))
390 (deps (mapm %store-monad instance->derivation
391 (edges instance))))
392 (build-channel-instance instance core deps)))
393 instance
394 system)))
ed75bdf3 395
ab6025b5
LC
396 (unless core-instance
397 (let ((loc (and=> (any (compose channel-location channel-instance-channel)
398 instances)
399 source-properties->location)))
400 (raise (apply make-compound-condition
401 (condition
402 (&message (message "'guix' channel is lacking")))
403 (if loc
404 (list (condition (&error-location (location loc))))
405 '())))))
406
ed75bdf3 407 (mapm %store-monad instance->derivation instances))
0d39a3b9 408
0d39a3b9
LC
409(define (whole-package-for-legacy name modules)
410 "Return a full-blown Guix package for MODULES, a derivation that builds Guix
411modules in the old ~/.config/guix/latest style."
412 (define packages
413 (resolve-interface '(gnu packages guile)))
414
49c35bbb
LC
415 (define modules+compiled
416 ;; Since MODULES contains both .scm and .go files at its root, re-bundle
417 ;; it so that it has share/guile/site and lib/guile, which is what
418 ;; 'whole-package' expects.
419 (computed-file (derivation-name modules)
420 (with-imported-modules '((guix build utils))
421 #~(begin
422 (use-modules (guix build utils))
423
424 (define version
425 (effective-version))
426 (define share
427 (string-append #$output "/share/guile/site"))
428 (define lib
429 (string-append #$output "/lib/guile/" version))
430
431 (mkdir-p share) (mkdir-p lib)
432 (symlink #$modules (string-append share "/" version))
433 (symlink #$modules (string-append lib "/site-ccache"))))))
434
0d39a3b9
LC
435 (letrec-syntax ((list (syntax-rules (->)
436 ((_)
437 '())
438 ((_ (module -> variable) rest ...)
439 (cons (module-ref (resolve-interface
440 '(gnu packages module))
441 'variable)
442 (list rest ...)))
443 ((_ variable rest ...)
444 (cons (module-ref packages 'variable)
445 (list rest ...))))))
49c35bbb 446 (whole-package name modules+compiled
0d39a3b9
LC
447
448 ;; In the "old style", %SELF-BUILD-FILE would simply return a
449 ;; derivation that builds modules. We have to infer what the
450 ;; dependencies of these modules were.
451 (list guile-json guile-git guile-bytestructures
452 (ssh -> guile-ssh) (tls -> gnutls)))))
453
454(define (old-style-guix? drv)
455 "Return true if DRV corresponds to a ~/.config/guix/latest style of
456derivation."
457 ;; Here we rely on a gross historical fact: that derivations produced by the
458 ;; "old style" (before commit 8a0d9bc8a3f153159d9e239a151c0fa98f1e12d8,
459 ;; dated May 30, 2018) did not depend on "guix-command.drv".
460 (not (find (lambda (input)
9af75a26
LC
461 (string=? "guix-command"
462 (derivation-name
463 (derivation-input-derivation input))))
0d39a3b9
LC
464 (derivation-inputs drv))))
465
466(define (channel-instances->manifest instances)
467 "Return a profile manifest with entries for all of INSTANCES, a list of
468channel instances."
d9e6217f
LC
469 (define (instance->entry instance drv)
470 (let ((commit (channel-instance-commit instance))
471 (channel (channel-instance-channel instance)))
472 (manifest-entry
473 (name (symbol->string (channel-name channel)))
474 (version (string-take commit 7))
475 (item (if (guix-channel? channel)
476 (if (old-style-guix? drv)
477 (whole-package-for-legacy (string-append name "-" version)
478 drv)
479 drv)
480 drv))
481 (properties
482 `((source (repository
483 (version 0)
484 (url ,(channel-url channel))
485 (branch ,(channel-branch channel))
486 (commit ,commit))))))))
0d39a3b9
LC
487
488 (mlet* %store-monad ((derivations (channel-instance-derivations instances))
d9e6217f 489 (entries -> (map instance->entry instances derivations)))
0d39a3b9 490 (return (manifest entries))))
030f1367 491
5fbdc9a5
LC
492(define (package-cache-file manifest)
493 "Build a package cache file for the instance in MANIFEST. This is meant to
494be used as a profile hook."
495 (mlet %store-monad ((profile (profile-derivation manifest
496 #:hooks '())))
497
498 (define build
499 #~(begin
500 (use-modules (gnu packages))
501
502 (if (defined? 'generate-package-cache)
503 (begin
504 ;; Delegate package cache generation to the inferior.
505 (format (current-error-port)
506 "Generating package cache for '~a'...~%"
507 #$profile)
508 (generate-package-cache #$output))
509 (mkdir #$output))))
510
511 (gexp->derivation-in-inferior "guix-package-cache" build
512 profile
4035fcba
LC
513
514 ;; If the Guix in PROFILE is too old and
515 ;; lacks 'guix repl', don't build the cache
516 ;; instead of failing.
517 #:silent-failure? #t
518
5fbdc9a5 519 #:properties '((type . profile-hook)
f674bc66
LC
520 (hook . package-cache))
521 #:local-build? #t)))
5fbdc9a5
LC
522
523(define %channel-profile-hooks
524 ;; The default channel profile hooks.
525 (cons package-cache-file %default-profile-hooks))
526
c37f38bd
LC
527(define (channel-instances->derivation instances)
528 "Return the derivation of the profile containing INSTANCES, a list of
529channel instances."
530 (mlet %store-monad ((manifest (channel-instances->manifest instances)))
5fbdc9a5
LC
531 (profile-derivation manifest
532 #:hooks %channel-profile-hooks)))
c37f38bd 533
030f1367
LC
534(define latest-channel-instances*
535 (store-lift latest-channel-instances))
536
537(define* (latest-channel-derivation #:optional (channels %default-channels))
538 "Return as a monadic value the derivation that builds the profile for the
539latest instances of CHANNELS."
c37f38bd
LC
540 (mlet %store-monad ((instances (latest-channel-instances* channels)))
541 (channel-instances->derivation instances)))