self: 'compiled-files' builds the given list of files.
[jackhill/guix/guix.git] / guix / self.scm
CommitLineData
eaae07ec
LC
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2017, 2018 Ludovic Courtès <ludo@gnu.org>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (guix self)
20 #:use-module (guix config)
21 #:use-module (guix i18n)
22 #:use-module (guix modules)
23 #:use-module (guix gexp)
24 #:use-module (guix store)
25 #:use-module (guix monads)
26 #:use-module (guix discovery)
27 #:use-module (guix packages)
28 #:use-module (guix sets)
29 #:use-module (guix utils)
30 #:use-module (guix modules)
31 #:use-module (guix build utils)
32 #:use-module ((guix build compile) #:select (%lightweight-optimizations))
33 #:use-module (srfi srfi-1)
34 #:use-module (srfi srfi-9)
35 #:use-module (ice-9 match)
36 #:export (make-config.scm
37 compiled-guix
38 guix-derivation
39 reload-guix))
40
41\f
42;;;
43;;; Dependency handling.
44;;;
45
46(define* (false-if-wrong-guile package
47 #:optional (guile-version (effective-version)))
48 "Return #f if PACKAGE depends on the \"wrong\" major version of Guile (e.g.,
492.0 instead of 2.2), otherwise return PACKAGE."
50 (let ((guile (any (match-lambda
51 ((label (? package? dep) _ ...)
52 (and (string=? (package-name dep) "guile")
53 dep)))
54 (package-direct-inputs package))))
55 (and (or (not guile)
56 (string-prefix? guile-version
57 (package-version guile)))
58 package)))
59
60(define (package-for-guile guile-version . names)
61 "Return the package with one of the given NAMES that depends on
62GUILE-VERSION (\"2.0\" or \"2.2\"), or #f if none of the packages matches."
63 (let loop ((names names))
64 (match names
65 (()
66 #f)
67 ((name rest ...)
68 (match (specification->package name)
69 (#f
70 (loop rest))
71 ((? package? package)
a1639ae9 72 (or (false-if-wrong-guile package guile-version)
eaae07ec
LC
73 (loop rest))))))))
74
75(define specification->package
76 ;; Use our own variant of that procedure because that of (gnu packages)
77 ;; would traverse all the .scm files, which is wasteful.
78 (let ((ref (lambda (module variable)
79 (module-ref (resolve-interface module) variable))))
80 (match-lambda
81 ("guile" (ref '(gnu packages commencement) 'guile-final))
82 ("guile-json" (ref '(gnu packages guile) 'guile-json))
83 ("guile-ssh" (ref '(gnu packages ssh) 'guile-ssh))
84 ("guile-git" (ref '(gnu packages guile) 'guile-git))
6d52844c 85 ("guile-gdbm-ffi" (ref '(gnu packages guile) 'guile-gdbm-ffi))
d59e75f3 86 ("guile-sqlite3" (ref '(gnu packages guile) 'guile-sqlite3))
eaae07ec
LC
87 ("libgcrypt" (ref '(gnu packages gnupg) 'libgcrypt))
88 ("zlib" (ref '(gnu packages compression) 'zlib))
89 ("gzip" (ref '(gnu packages compression) 'gzip))
90 ("bzip2" (ref '(gnu packages compression) 'bzip2))
91 ("xz" (ref '(gnu packages compression) 'xz))
92 ("guix" (ref '(gnu packages package-management)
e69dd844
LC
93 'guix-register))
94 ("guile2.0-json" (ref '(gnu packages guile) 'guile2.0-json))
95 ("guile2.0-ssh" (ref '(gnu packages ssh) 'guile2.0-ssh))
96 ("guile2.0-git" (ref '(gnu packages guile) 'guile2.0-git))
6d52844c 97 ("guile2.0-gdbm-ffi" (ref '(gnu packages guile) 'guile2.0-gdbm-ffi))
d59e75f3 98 ;; XXX: No "guile2.0-sqlite3".
e69dd844 99 (_ #f)))) ;no such package
eaae07ec
LC
100
101\f
102;;;
103;;; Derivations.
104;;;
105
106;; Node in a DAG of build tasks. Each node maps to a derivation, but it's
107;; easier to express things this way.
108(define-record-type <node>
109 (node name modules source dependencies compiled)
110 node?
111 (name node-name) ;string
112 (modules node-modules) ;list of module names
113 (source node-source) ;list of source files
114 (dependencies node-dependencies) ;list of nodes
115 (compiled node-compiled)) ;node -> lowerable object
116
117(define (node-fold proc init nodes)
118 (let loop ((nodes nodes)
119 (visited (setq))
120 (result init))
121 (match nodes
122 (() result)
123 ((head tail ...)
124 (if (set-contains? visited head)
125 (loop tail visited result)
126 (loop tail (set-insert head visited)
127 (proc head result)))))))
128
129(define (node-modules/recursive nodes)
130 (node-fold (lambda (node modules)
131 (append (node-modules node) modules))
132 '()
133 nodes))
134
135(define* (closure modules #:optional (except '()))
136 (source-module-closure modules
137 #:select?
138 (match-lambda
139 (('guix 'config)
140 #f)
141 ((and module
142 (or ('guix _ ...) ('gnu _ ...)))
143 (not (member module except)))
144 (rest #f))))
145
146(define module->import
147 ;; Return a file-name/file-like object pair for the specified module and
148 ;; suitable for 'imported-files'.
149 (match-lambda
150 ((module '=> thing)
151 (let ((file (module-name->file-name module)))
152 (list file thing)))
153 (module
154 (let ((file (module-name->file-name module)))
155 (list file
156 (local-file (search-path %load-path file)))))))
157
158(define* (scheme-node name modules #:optional (dependencies '())
159 #:key (extra-modules '()) (extra-files '())
160 (extensions '())
161 parallel? guile-for-build)
162 "Return a node that builds the given Scheme MODULES, and depends on
163DEPENDENCIES (a list of nodes). EXTRA-MODULES is a list of additional modules
164added to the source, and EXTRA-FILES is a list of additional files.
165EXTENSIONS is a set of full-blown Guile packages (e.g., 'guile-json') that
166must be present in the search path."
167 (let* ((modules (append extra-modules
168 (closure modules
169 (node-modules/recursive dependencies))))
170 (module-files (map module->import modules))
171 (source (imported-files (string-append name "-source")
172 (append module-files extra-files))))
173 (node name modules source dependencies
8031b3fa
LC
174 (compiled-modules name source
175 (map car module-files)
eaae07ec
LC
176 (map node-source dependencies)
177 (map node-compiled dependencies)
178 #:extensions extensions
179 #:parallel? parallel?
180 #:guile-for-build guile-for-build))))
181
182(define (file-imports directory sub-directory pred)
183 "List all the files matching PRED under DIRECTORY/SUB-DIRECTORY. Return a
184list of file-name/file-like objects suitable as inputs to 'imported-files'."
185 (map (lambda (file)
186 (list (string-drop file (+ 1 (string-length directory)))
187 (local-file file #:recursive? #t)))
188 (find-files (string-append directory "/" sub-directory) pred)))
189
190(define (scheme-modules* directory sub-directory)
191 "Return the list of module names found under SUB-DIRECTORY in DIRECTORY."
192 (let ((prefix (string-length directory)))
193 (map (lambda (file)
194 (file-name->module-name (string-drop file prefix)))
195 (scheme-files (string-append directory "/" sub-directory)))))
196
197(define* (compiled-guix source #:key (version %guix-version)
198 (name (string-append "guix-" version))
199 (guile-version (effective-version))
200 (guile-for-build (guile-for-build guile-version))
201 (libgcrypt (specification->package "libgcrypt"))
202 (zlib (specification->package "zlib"))
203 (gzip (specification->package "gzip"))
204 (bzip2 (specification->package "bzip2"))
205 (xz (specification->package "xz"))
206 (guix (specification->package "guix")))
207 "Return a file-like object that contains a compiled Guix."
208 (define guile-json
209 (package-for-guile guile-version
210 "guile-json"
eaae07ec
LC
211 "guile2.0-json"))
212
213 (define guile-ssh
214 (package-for-guile guile-version
215 "guile-ssh"
eaae07ec
LC
216 "guile2.0-ssh"))
217
218 (define guile-git
219 (package-for-guile guile-version
220 "guile-git"
221 "guile2.0-git"))
222
6d52844c
LC
223 (define guile-gdbm-ffi
224 (package-for-guile guile-version
225 "guile-gdbm-ffi"
226 "guile2.0-gdbm-ffi"))
227
eaae07ec 228
d59e75f3
LC
229 (define guile-sqlite3
230 (package-for-guile guile-version
231 "guile-sqlite3"
232 "guile2.0-sqlite3"))
233
eaae07ec
LC
234 (define dependencies
235 (match (append-map (lambda (package)
236 (cons (list "x" package)
e13240f5 237 (package-transitive-propagated-inputs package)))
6d52844c
LC
238 (list guile-git guile-json guile-ssh
239 guile-gdbm-ffi guile-sqlite3))
eaae07ec
LC
240 (((labels packages _ ...) ...)
241 packages)))
242
243 (define *core-modules*
244 (scheme-node "guix-core"
245 '((guix)
246 (guix monad-repl)
247 (guix packages)
248 (guix download)
249 (guix discovery)
250 (guix profiles)
251 (guix build-system gnu)
252 (guix build-system trivial)
253 (guix build profiles)
254 (guix build gnu-build-system))
255
256 ;; Provide a dummy (guix config) with the default version
257 ;; number, storedir, etc. This is so that "guix-core" is the
258 ;; same across all installations and doesn't need to be
259 ;; rebuilt when the version changes, which in turn means we
260 ;; can have substitutes for it.
261 #:extra-modules
262 `(((guix config)
263 => ,(make-config.scm #:libgcrypt
264 (specification->package
265 "libgcrypt"))))
266
267 #:guile-for-build guile-for-build))
268
269 (define *extra-modules*
270 (scheme-node "guix-extra"
271 (filter-map (match-lambda
272 (('guix 'scripts _ ..1) #f)
273 (name name))
274 (scheme-modules* source "guix"))
275 (list *core-modules*)
276 #:extensions dependencies
277 #:guile-for-build guile-for-build))
278
f2e66663
LC
279 (define *core-package-modules*
280 (scheme-node "guix-packages-base"
eaae07ec 281 `((gnu packages)
f2e66663 282 (gnu packages base))
eaae07ec
LC
283 (list *core-modules* *extra-modules*)
284 #:extensions dependencies
f2e66663
LC
285
286 ;; Add all the non-Scheme files here. We must do it here so
287 ;; that 'search-patches' & co. can find them. Ideally we'd
288 ;; keep them next to the .scm files that use them but it's
289 ;; difficult to do (XXX).
290 #:extra-files
eaae07ec
LC
291 (file-imports source "gnu/packages"
292 (lambda (file stat)
293 (and (eq? 'regular (stat:type stat))
294 (not (string-suffix? ".scm" file))
295 (not (string-suffix? ".go" file))
296 (not (string-prefix? ".#" file))
297 (not (string-suffix? "~" file)))))
298 #:guile-for-build guile-for-build))
299
f2e66663
LC
300 (define *package-modules*
301 (scheme-node "guix-packages"
302 (scheme-modules* source "gnu/packages")
303 (list *core-modules* *extra-modules* *core-package-modules*)
304 #:extensions dependencies
305 #:guile-for-build guile-for-build))
306
eaae07ec
LC
307 (define *system-modules*
308 (scheme-node "guix-system"
309 `((gnu system)
310 (gnu services)
311 ,@(scheme-modules* source "gnu/system")
312 ,@(scheme-modules* source "gnu/services"))
f2e66663
LC
313 (list *core-package-modules* *package-modules*
314 *extra-modules* *core-modules*)
eaae07ec
LC
315 #:extensions dependencies
316 #:extra-files
317 (file-imports source "gnu/system/examples" (const #t))
318 #:guile-for-build
319 guile-for-build))
320
321 (define *cli-modules*
322 (scheme-node "guix-cli"
323 (scheme-modules* source "/guix/scripts")
f2e66663
LC
324 (list *core-modules* *extra-modules*
325 *core-package-modules* *package-modules*
eaae07ec
LC
326 *system-modules*)
327 #:extensions dependencies
328 #:guile-for-build guile-for-build))
329
330 (define *config*
331 (scheme-node "guix-config"
332 '()
333 #:extra-modules
334 `(((guix config)
335 => ,(make-config.scm #:libgcrypt libgcrypt
336 #:zlib zlib
337 #:gzip gzip
338 #:bzip2 bzip2
339 #:xz xz
340 #:guix guix
341 #:package-name
342 %guix-package-name
343 #:package-version
344 version
345 #:bug-report-address
346 %guix-bug-report-address
347 #:home-page-url
348 %guix-home-page-url)))
349 #:guile-for-build guile-for-build))
350
351 (directory-union name
352 (append-map (lambda (node)
353 (list (node-source node)
354 (node-compiled node)))
355
356 ;; Note: *CONFIG* comes first so that it
357 ;; overrides the (guix config) module that
358 ;; comes with *CORE-MODULES*.
359 (list *config*
360 *cli-modules*
361 *system-modules*
362 *package-modules*
f2e66663 363 *core-package-modules*
eaae07ec
LC
364 *extra-modules*
365 *core-modules*))
366
367 ;; Silently choose the first entry upon collision so that
368 ;; we choose *CONFIG*.
369 #:resolve-collision 'first
370
371 ;; When we do (add-to-store "utils.scm"), "utils.scm" must
372 ;; be a regular file, not a symlink. Thus, arrange so that
373 ;; regular files appear as regular files in the final
374 ;; output.
375 #:copy? #t
376 #:quiet? #t))
377
378\f
379;;;
380;;; Generating (guix config).
381;;;
382
383(define %dependency-variables
384 ;; (guix config) variables corresponding to dependencies.
385 '(%libgcrypt %libz %xz %gzip %bzip2 %nix-instantiate
386 %sbindir %guix-register-program))
387
388(define %persona-variables
389 ;; (guix config) variables that define Guix's persona.
390 '(%guix-package-name
391 %guix-version
392 %guix-bug-report-address
393 %guix-home-page-url))
394
395(define %config-variables
396 ;; (guix config) variables corresponding to Guix configuration (storedir,
397 ;; localstatedir, etc.)
398 (sort (filter pair?
399 (module-map (lambda (name var)
400 (and (not (memq name %dependency-variables))
401 (not (memq name %persona-variables))
402 (cons name (variable-ref var))))
403 (resolve-interface '(guix config))))
404 (lambda (name+value1 name+value2)
405 (string<? (symbol->string (car name+value1))
406 (symbol->string (car name+value2))))))
407
408(define* (make-config.scm #:key libgcrypt zlib gzip xz bzip2 guix
409 (package-name "GNU Guix")
410 (package-version "0")
411 (bug-report-address "bug-guix@gnu.org")
412 (home-page-url "https://gnu.org/s/guix"))
413
414 ;; Hack so that Geiser is not confused.
415 (define defmod 'define-module)
416
417 (scheme-file "config.scm"
eb72cdf0 418 #~(;; The following expressions get spliced.
eaae07ec
LC
419 (#$defmod (guix config)
420 #:export (%guix-package-name
421 %guix-version
422 %guix-bug-report-address
423 %guix-home-page-url
424 %sbindir
806ff358 425 %guix-register-program
eaae07ec
LC
426 %libgcrypt
427 %libz
428 %gzip
429 %bzip2
430 %xz
431 %nix-instantiate))
432
63cab418
LC
433 #$@(map (match-lambda
434 ((name . value)
435 #~(define-public #$name #$value)))
436 %config-variables)
437
438 (define %guix-package-name #$package-name)
439 (define %guix-version #$package-version)
440 (define %guix-bug-report-address #$bug-report-address)
441 (define %guix-home-page-url #$home-page-url)
442
443 (define %sbindir
444 ;; This is used to define '%guix-register-program'.
445 ;; TODO: Use a derivation that builds nothing but the
446 ;; C++ part.
447 #+(and guix (file-append guix "/sbin")))
448
449 (define %guix-register-program
450 (or (getenv "GUIX_REGISTER")
451 (and %sbindir
452 (string-append %sbindir "/guix-register"))))
453
454 (define %gzip
455 #+(and gzip (file-append gzip "/bin/gzip")))
456 (define %bzip2
457 #+(and bzip2 (file-append bzip2 "/bin/bzip2")))
458 (define %xz
459 #+(and xz (file-append xz "/bin/xz")))
460
461 (define %libgcrypt
462 #+(and libgcrypt
463 (file-append libgcrypt "/lib/libgcrypt")))
464 (define %libz
465 #+(and zlib
466 (file-append zlib "/lib/libz")))
467
468 (define %nix-instantiate ;for (guix import snix)
eb72cdf0
LC
469 "nix-instantiate"))
470
471 ;; Guile 2.0 *requires* the 'define-module' to be at the
472 ;; top-level or it 'toplevel-ref' in the resulting .go file are
473 ;; made relative to a nonexistent anonymous module.
474 #:splice? #t))
eaae07ec
LC
475
476
477\f
478;;;
479;;; Building.
480;;;
481
482(define (imported-files name files)
483 ;; This is a non-monadic, simplified version of 'imported-files' from (guix
484 ;; gexp).
485 (define build
486 (with-imported-modules (source-module-closure
487 '((guix build utils)))
488 #~(begin
489 (use-modules (ice-9 match)
490 (guix build utils))
491
492 (mkdir (ungexp output)) (chdir (ungexp output))
493 (for-each (match-lambda
494 ((final-path store-path)
495 (mkdir-p (dirname final-path))
496
497 ;; Note: We need regular files to be regular files, not
498 ;; symlinks, as this makes a difference for
499 ;; 'add-to-store'.
500 (copy-file store-path final-path)))
501 '#$files))))
502
14b392a8
LC
503 ;; We're just copying files around, no need to substitute or offload it.
504 (computed-file name build
505 #:options '(#:local-build? #t
e3a87d77
LC
506 #:substitutable? #f
507 #:env-vars (("COLUMNS" . "200")))))
eaae07ec 508
8031b3fa 509(define* (compiled-modules name module-tree module-files
eaae07ec
LC
510 #:optional
511 (dependencies '())
512 (dependencies-compiled '())
513 #:key
514 (extensions '()) ;full-blown Guile packages
515 parallel?
516 guile-for-build)
8031b3fa
LC
517 "Build all the MODULE-FILES from MODULE-TREE. MODULE-FILES must be a list
518like '(\"guix/foo.scm\" \"gnu/bar.scm\") and MODULE-TREE is the directory
519containing MODULE-FILES and possibly other files as well."
eaae07ec
LC
520 ;; This is a non-monadic, enhanced version of 'compiled-file' from (guix
521 ;; gexp).
522 (define build
523 (with-imported-modules (source-module-closure
524 '((guix build compile)
525 (guix build utils)))
526 #~(begin
527 (use-modules (srfi srfi-26)
528 (ice-9 match)
529 (ice-9 format)
530 (ice-9 threads)
531 (guix build compile)
532 (guix build utils))
533
534 (define (regular? file)
535 (not (member file '("." ".."))))
536
537 (define (report-load file total completed)
538 (display #\cr)
539 (format #t
540 "loading...\t~5,1f% of ~d files" ;FIXME: i18n
541 (* 100. (/ completed total)) total)
542 (force-output))
543
544 (define (report-compilation file total completed)
545 (display #\cr)
546 (format #t "compiling...\t~5,1f% of ~d files" ;FIXME: i18n
547 (* 100. (/ completed total)) total)
548 (force-output))
549
8031b3fa
LC
550 (define (process-directory directory files output)
551 ;; Hide compilation warnings.
552 (parameterize ((current-warning-port (%make-void-port "w")))
553 (compile-files directory #$output files
554 #:workers (parallel-job-count)
555 #:report-load report-load
556 #:report-compilation report-compilation)))
eaae07ec
LC
557
558 (setvbuf (current-output-port) _IONBF)
559 (setvbuf (current-error-port) _IONBF)
560
561 (set! %load-path (cons #+module-tree %load-path))
562 (set! %load-path
563 (append '#+dependencies
564 (map (lambda (extension)
565 (string-append extension "/share/guile/site/"
566 (effective-version)))
567 '#+extensions)
568 %load-path))
569
570 (set! %load-compiled-path
571 (append '#+dependencies-compiled
572 (map (lambda (extension)
573 (string-append extension "/lib/guile/"
574 (effective-version)
575 "/site-ccache"))
576 '#+extensions)
577 %load-compiled-path))
578
579 ;; Load the compiler modules upfront.
580 (compile #f)
581
582 (mkdir #$output)
583 (chdir #+module-tree)
8031b3fa 584 (process-directory "." '#+module-files #$output)
69447b63 585 (newline))))
eaae07ec
LC
586
587 (computed-file name build
588 #:guile guile-for-build
589 #:options
590 `(#:local-build? #f ;allow substitutes
591
592 ;; Don't annoy people about _IONBF deprecation.
54be2b4d
LC
593 ;; Initialize 'terminal-width' in (system repl debug)
594 ;; to a large-enough value to make backtrace more
595 ;; verbose.
596 #:env-vars (("GUILE_WARN_DEPRECATED" . "no")
597 ("COLUMNS" . "200")))))
eaae07ec
LC
598
599\f
600;;;
601;;; Building.
602;;;
603
604(define (guile-for-build version)
605 "Return a derivation for Guile 2.0 or 2.2, whichever matches the currently
606running Guile."
607 (define canonical-package ;soft reference
608 (module-ref (resolve-interface '(gnu packages base))
609 'canonical-package))
610
611 (match version
612 ("2.2.2"
613 ;; Gross hack to avoid ABI incompatibilities (see
614 ;; <https://bugs.gnu.org/29570>.)
615 (module-ref (resolve-interface '(gnu packages guile))
616 'guile-2.2.2))
617 ("2.2"
618 (canonical-package (module-ref (resolve-interface '(gnu packages guile))
619 'guile-2.2/fixed)))
620 ("2.0"
e69dd844
LC
621 (module-ref (resolve-interface '(gnu packages guile))
622 'guile-2.0))))
eaae07ec
LC
623
624(define* (guix-derivation source version
625 #:optional (guile-version (effective-version)))
626 "Return, as a monadic value, the derivation to build the Guix from SOURCE
627for GUILE-VERSION. Use VERSION as the version string."
628 (define (shorten version)
629 (if (and (string-every char-set:hex-digit version)
630 (> (string-length version) 9))
631 (string-take version 9) ;Git commit
632 version))
633
634 (define guile
635 (guile-for-build guile-version))
636
637 (mbegin %store-monad
638 (set-guile-for-build guile)
639 (lower-object (compiled-guix source
640 #:version version
641 #:name (string-append "guix-"
642 (shorten version))
643 #:guile-version (match guile-version
644 ("2.2.2" "2.2")
645 (version version))
646 #:guile-for-build guile))))