summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--gnu/packages.scm21
-rw-r--r--guix/discovery.scm3
-rw-r--r--guix/self.scm599
4 files changed, 621 insertions, 3 deletions
diff --git a/Makefile.am b/Makefile.am
index 244069b5337..e4edd05d727 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -81,6 +81,7 @@ MODULES = \
81 guix/derivations.scm \ 81 guix/derivations.scm \
82 guix/grafts.scm \ 82 guix/grafts.scm \
83 guix/gnu-maintenance.scm \ 83 guix/gnu-maintenance.scm \
84 guix/self.scm \
84 guix/upstream.scm \ 85 guix/upstream.scm \
85 guix/licenses.scm \ 86 guix/licenses.scm \
86 guix/glob.scm \ 87 guix/glob.scm \
diff --git a/gnu/packages.scm b/gnu/packages.scm
index 97e6cb347f2..44a56dfde07 100644
--- a/gnu/packages.scm
+++ b/gnu/packages.scm
@@ -110,8 +110,25 @@ for system '~a'")
110 file-name system))))))) 110 file-name system)))))))
111 111
112(define %distro-root-directory 112(define %distro-root-directory
113 ;; Absolute file name of the module hierarchy. 113 ;; Absolute file name of the module hierarchy. Since (gnu packages …) might
114 (dirname (search-path %load-path "guix.scm"))) 114 ;; live in a directory different from (guix), try to get the best match.
115 (letrec-syntax ((dirname* (syntax-rules ()
116 ((_ file)
117 (dirname file))
118 ((_ file head tail ...)
119 (dirname (dirname* file tail ...)))))
120 (try (syntax-rules ()
121 ((_ (file things ...) rest ...)
122 (match (search-path %load-path file)
123 (#f
124 (try rest ...))
125 (absolute
126 (dirname* absolute things ...))))
127 ((_)
128 #f))))
129 (try ("gnu/packages/base.scm" gnu/ packages/)
130 ("gnu/packages.scm" gnu/)
131 ("guix.scm"))))
115 132
116(define %package-module-path 133(define %package-module-path
117 ;; Search path for package modules. Each item must be either a directory 134 ;; Search path for package modules. Each item must be either a directory
diff --git a/guix/discovery.scm b/guix/discovery.scm
index 7b575790237..8ffcf7cd9ab 100644
--- a/guix/discovery.scm
+++ b/guix/discovery.scm
@@ -25,7 +25,8 @@
25 #:use-module (ice-9 match) 25 #:use-module (ice-9 match)
26 #:use-module (ice-9 vlist) 26 #:use-module (ice-9 vlist)
27 #:use-module (ice-9 ftw) 27 #:use-module (ice-9 ftw)
28 #:export (scheme-modules 28 #:export (scheme-files
29 scheme-modules
29 fold-modules 30 fold-modules
30 all-modules 31 all-modules
31 fold-module-public-variables)) 32 fold-module-public-variables))
diff --git a/guix/self.scm b/guix/self.scm
new file mode 100644
index 00000000000..c9e4a4250eb
--- /dev/null
+++ b/guix/self.scm
@@ -0,0 +1,599 @@
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
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)
72 (or (false-if-wrong-guile package)
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))
85 ("libgcrypt" (ref '(gnu packages gnupg) 'libgcrypt))
86 ("zlib" (ref '(gnu packages compression) 'zlib))
87 ("gzip" (ref '(gnu packages compression) 'gzip))
88 ("bzip2" (ref '(gnu packages compression) 'bzip2))
89 ("xz" (ref '(gnu packages compression) 'xz))
90 ("guix" (ref '(gnu packages package-management)
91 'guix-register)))))
92
93
94;;;
95;;; Derivations.
96;;;
97
98;; Node in a DAG of build tasks. Each node maps to a derivation, but it's
99;; easier to express things this way.
100(define-record-type <node>
101 (node name modules source dependencies compiled)
102 node?
103 (name node-name) ;string
104 (modules node-modules) ;list of module names
105 (source node-source) ;list of source files
106 (dependencies node-dependencies) ;list of nodes
107 (compiled node-compiled)) ;node -> lowerable object
108
109(define (node-fold proc init nodes)
110 (let loop ((nodes nodes)
111 (visited (setq))
112 (result init))
113 (match nodes
114 (() result)
115 ((head tail ...)
116 (if (set-contains? visited head)
117 (loop tail visited result)
118 (loop tail (set-insert head visited)
119 (proc head result)))))))
120
121(define (node-modules/recursive nodes)
122 (node-fold (lambda (node modules)
123 (append (node-modules node) modules))
124 '()
125 nodes))
126
127(define* (closure modules #:optional (except '()))
128 (source-module-closure modules
129 #:select?
130 (match-lambda
131 (('guix 'config)
132 #f)
133 ((and module
134 (or ('guix _ ...) ('gnu _ ...)))
135 (not (member module except)))
136 (rest #f))))
137
138(define module->import
139 ;; Return a file-name/file-like object pair for the specified module and
140 ;; suitable for 'imported-files'.
141 (match-lambda
142 ((module '=> thing)
143 (let ((file (module-name->file-name module)))
144 (list file thing)))
145 (module
146 (let ((file (module-name->file-name module)))
147 (list file
148 (local-file (search-path %load-path file)))))))
149
150(define* (scheme-node name modules #:optional (dependencies '())
151 #:key (extra-modules '()) (extra-files '())
152 (extensions '())
153 parallel? guile-for-build)
154 "Return a node that builds the given Scheme MODULES, and depends on
155DEPENDENCIES (a list of nodes). EXTRA-MODULES is a list of additional modules
156added to the source, and EXTRA-FILES is a list of additional files.
157EXTENSIONS is a set of full-blown Guile packages (e.g., 'guile-json') that
158must be present in the search path."
159 (let* ((modules (append extra-modules
160 (closure modules
161 (node-modules/recursive dependencies))))
162 (module-files (map module->import modules))
163 (source (imported-files (string-append name "-source")
164 (append module-files extra-files))))
165 (node name modules source dependencies
166 (compiled-modules name source modules
167 (map node-source dependencies)
168 (map node-compiled dependencies)
169 #:extensions extensions
170 #:parallel? parallel?
171 #:guile-for-build guile-for-build))))
172
173(define (file-imports directory sub-directory pred)
174 "List all the files matching PRED under DIRECTORY/SUB-DIRECTORY. Return a
175list of file-name/file-like objects suitable as inputs to 'imported-files'."
176 (map (lambda (file)
177 (list (string-drop file (+ 1 (string-length directory)))
178 (local-file file #:recursive? #t)))
179 (find-files (string-append directory "/" sub-directory) pred)))
180
181(define (scheme-modules* directory sub-directory)
182 "Return the list of module names found under SUB-DIRECTORY in DIRECTORY."
183 (let ((prefix (string-length directory)))
184 (map (lambda (file)
185 (file-name->module-name (string-drop file prefix)))
186 (scheme-files (string-append directory "/" sub-directory)))))
187
188(define* (compiled-guix source #:key (version %guix-version)
189 (name (string-append "guix-" version))
190 (guile-version (effective-version))
191 (guile-for-build (guile-for-build guile-version))
192 (libgcrypt (specification->package "libgcrypt"))
193 (zlib (specification->package "zlib"))
194 (gzip (specification->package "gzip"))
195 (bzip2 (specification->package "bzip2"))
196 (xz (specification->package "xz"))
197 (guix (specification->package "guix")))
198 "Return a file-like object that contains a compiled Guix."
199 (define guile-json
200 (package-for-guile guile-version
201 "guile-json"
202 "guile2.2-json"
203 "guile2.0-json"))
204
205 (define guile-ssh
206 (package-for-guile guile-version
207 "guile-ssh"
208 "guile2.2-ssh"
209 "guile2.0-ssh"))
210
211 (define guile-git
212 (package-for-guile guile-version
213 "guile-git"
214 "guile2.0-git"))
215
216
217 (define dependencies
218 (match (append-map (lambda (package)
219 (cons (list "x" package)
220 (package-transitive-inputs package)))
221 (list guile-git guile-json guile-ssh))
222 (((labels packages _ ...) ...)
223 packages)))
224
225 (define *core-modules*
226 (scheme-node "guix-core"
227 '((guix)
228 (guix monad-repl)
229 (guix packages)
230 (guix download)
231 (guix discovery)
232 (guix profiles)
233 (guix build-system gnu)
234 (guix build-system trivial)
235 (guix build profiles)
236 (guix build gnu-build-system))
237
238 ;; Provide a dummy (guix config) with the default version
239 ;; number, storedir, etc. This is so that "guix-core" is the
240 ;; same across all installations and doesn't need to be
241 ;; rebuilt when the version changes, which in turn means we
242 ;; can have substitutes for it.
243 #:extra-modules
244 `(((guix config)
245 => ,(make-config.scm #:libgcrypt
246 (specification->package
247 "libgcrypt"))))
248
249 #:guile-for-build guile-for-build))
250
251 (define *extra-modules*
252 (scheme-node "guix-extra"
253 (filter-map (match-lambda
254 (('guix 'scripts _ ..1) #f)
255 (name name))
256 (scheme-modules* source "guix"))
257 (list *core-modules*)
258 #:extensions dependencies
259 #:guile-for-build guile-for-build))
260
261 (define *package-modules*
262 (scheme-node "guix-packages"
263 `((gnu packages)
264 ,@(scheme-modules* source "gnu/packages"))
265 (list *core-modules* *extra-modules*)
266 #:extensions dependencies
267 #:extra-files ;all the non-Scheme files
268 (file-imports source "gnu/packages"
269 (lambda (file stat)
270 (and (eq? 'regular (stat:type stat))
271 (not (string-suffix? ".scm" file))
272 (not (string-suffix? ".go" file))
273 (not (string-prefix? ".#" file))
274 (not (string-suffix? "~" file)))))
275 #:guile-for-build guile-for-build))
276
277 (define *system-modules*
278 (scheme-node "guix-system"
279 `((gnu system)
280 (gnu services)
281 ,@(scheme-modules* source "gnu/system")
282 ,@(scheme-modules* source "gnu/services"))
283 (list *package-modules* *extra-modules* *core-modules*)
284 #:extensions dependencies
285 #:extra-files
286 (file-imports source "gnu/system/examples" (const #t))
287 #:guile-for-build
288 guile-for-build))
289
290 (define *cli-modules*
291 (scheme-node "guix-cli"
292 (scheme-modules* source "/guix/scripts")
293 (list *core-modules* *extra-modules* *package-modules*
294 *system-modules*)
295 #:extensions dependencies
296 #:guile-for-build guile-for-build))
297
298 (define *config*
299 (scheme-node "guix-config"
300 '()
301 #:extra-modules
302 `(((guix config)
303 => ,(make-config.scm #:libgcrypt libgcrypt
304 #:zlib zlib
305 #:gzip gzip
306 #:bzip2 bzip2
307 #:xz xz
308 #:guix guix
309 #:package-name
310 %guix-package-name
311 #:package-version
312 version
313 #:bug-report-address
314 %guix-bug-report-address
315 #:home-page-url
316 %guix-home-page-url)))
317 #:guile-for-build guile-for-build))
318
319 (directory-union name
320 (append-map (lambda (node)
321 (list (node-source node)
322 (node-compiled node)))
323
324 ;; Note: *CONFIG* comes first so that it
325 ;; overrides the (guix config) module that
326 ;; comes with *CORE-MODULES*.
327 (list *config*
328 *cli-modules*
329 *system-modules*
330 *package-modules*
331 *extra-modules*
332 *core-modules*))
333
334 ;; Silently choose the first entry upon collision so that
335 ;; we choose *CONFIG*.
336 #:resolve-collision 'first
337
338 ;; When we do (add-to-store "utils.scm"), "utils.scm" must
339 ;; be a regular file, not a symlink. Thus, arrange so that
340 ;; regular files appear as regular files in the final
341 ;; output.
342 #:copy? #t
343 #:quiet? #t))
344
345
346;;;
347;;; Generating (guix config).
348;;;
349
350(define %dependency-variables
351 ;; (guix config) variables corresponding to dependencies.
352 '(%libgcrypt %libz %xz %gzip %bzip2 %nix-instantiate
353 %sbindir %guix-register-program))
354
355(define %persona-variables
356 ;; (guix config) variables that define Guix's persona.
357 '(%guix-package-name
358 %guix-version
359 %guix-bug-report-address
360 %guix-home-page-url))
361
362(define %config-variables
363 ;; (guix config) variables corresponding to Guix configuration (storedir,
364 ;; localstatedir, etc.)
365 (sort (filter pair?
366 (module-map (lambda (name var)
367 (and (not (memq name %dependency-variables))
368 (not (memq name %persona-variables))
369 (cons name (variable-ref var))))
370 (resolve-interface '(guix config))))
371 (lambda (name+value1 name+value2)
372 (string<? (symbol->string (car name+value1))
373 (symbol->string (car name+value2))))))
374
375(define* (make-config.scm #:key libgcrypt zlib gzip xz bzip2 guix
376 (package-name "GNU Guix")
377 (package-version "0")
378 (bug-report-address "bug-guix@gnu.org")
379 (home-page-url "https://gnu.org/s/guix"))
380
381 ;; Hack so that Geiser is not confused.
382 (define defmod 'define-module)
383
384 (scheme-file "config.scm"
385 #~(begin
386 (#$defmod (guix config)
387 #:export (%guix-package-name
388 %guix-version
389 %guix-bug-report-address
390 %guix-home-page-url
391 %sbindir
392 %libgcrypt
393 %libz
394 %gzip
395 %bzip2
396 %xz
397 %nix-instantiate))
398
399 ;; XXX: Work around <http://bugs.gnu.org/15602>.
400 (eval-when (expand load eval)
401 #$@(map (match-lambda
402 ((name . value)
403 #~(define-public #$name #$value)))
404 %config-variables)
405
406 (define %guix-package-name #$package-name)
407 (define %guix-version #$package-version)
408 (define %guix-bug-report-address #$bug-report-address)
409 (define %guix-home-page-url #$home-page-url)
410
411 (define %sbindir
412 ;; This is used to define '%guix-register-program'.
413 ;; TODO: Use a derivation that builds nothing but the
414 ;; C++ part.
415 #+(and guix (file-append guix "/sbin")))
416
417 (define %guix-register-program
418 (or (getenv "GUIX_REGISTER")
419 (and %sbindir
420 (string-append %sbindir "/guix-register"))))
421
422 (define %gzip
423 #+(and gzip (file-append gzip "/bin/gzip")))
424 (define %bzip2
425 #+(and bzip2 (file-append bzip2 "/bin/bzip2")))
426 (define %xz
427 #+(and xz (file-append xz "/bin/xz")))
428
429 (define %libgcrypt
430 #+(and libgcrypt
431 (file-append libgcrypt "/lib/libgcrypt")))
432 (define %libz
433 #+(and zlib
434 (file-append zlib "/lib/libz")))
435
436 (define %nix-instantiate ;for (guix import snix)
437 "nix-instantiate")))))
438
439
440
441;;;
442;;; Building.
443;;;
444
445(define (imported-files name files)
446 ;; This is a non-monadic, simplified version of 'imported-files' from (guix
447 ;; gexp).
448 (define build
449 (with-imported-modules (source-module-closure
450 '((guix build utils)))
451 #~(begin
452 (use-modules (ice-9 match)
453 (guix build utils))
454
455 (mkdir (ungexp output)) (chdir (ungexp output))
456 (for-each (match-lambda
457 ((final-path store-path)
458 (mkdir-p (dirname final-path))
459
460 ;; Note: We need regular files to be regular files, not
461 ;; symlinks, as this makes a difference for
462 ;; 'add-to-store'.
463 (copy-file store-path final-path)))
464 '#$files))))
465
466 (computed-file name build))
467
468(define* (compiled-modules name module-tree modules
469 #:optional
470 (dependencies '())
471 (dependencies-compiled '())
472 #:key
473 (extensions '()) ;full-blown Guile packages
474 parallel?
475 guile-for-build)
476 ;; This is a non-monadic, enhanced version of 'compiled-file' from (guix
477 ;; gexp).
478 (define build
479 (with-imported-modules (source-module-closure
480 '((guix build compile)
481 (guix build utils)))
482 #~(begin
483 (use-modules (srfi srfi-26)
484 (ice-9 match)
485 (ice-9 format)
486 (ice-9 threads)
487 (guix build compile)
488 (guix build utils))
489
490 (define (regular? file)
491 (not (member file '("." ".."))))
492
493 (define (report-load file total completed)
494 (display #\cr)
495 (format #t
496 "loading...\t~5,1f% of ~d files" ;FIXME: i18n
497 (* 100. (/ completed total)) total)
498 (force-output))
499
500 (define (report-compilation file total completed)
501 (display #\cr)
502 (format #t "compiling...\t~5,1f% of ~d files" ;FIXME: i18n
503 (* 100. (/ completed total)) total)
504 (force-output))
505
506 (define (process-directory directory output)
507 (let ((files (find-files directory "\\.scm$"))
508 (prefix (+ 1 (string-length directory))))
509 ;; Hide compilation warnings.
510 (parameterize ((current-warning-port (%make-void-port "w")))
511 (compile-files directory #$output
512 (map (cut string-drop <> prefix) files)
513 #:workers (parallel-job-count)
514 #:report-load report-load
515 #:report-compilation report-compilation))))
516
517 (setvbuf (current-output-port) _IONBF)
518 (setvbuf (current-error-port) _IONBF)
519
520 (set! %load-path (cons #+module-tree %load-path))
521 (set! %load-path
522 (append '#+dependencies
523 (map (lambda (extension)
524 (string-append extension "/share/guile/site/"
525 (effective-version)))
526 '#+extensions)
527 %load-path))
528
529 (set! %load-compiled-path
530 (append '#+dependencies-compiled
531 (map (lambda (extension)
532 (string-append extension "/lib/guile/"
533 (effective-version)
534 "/site-ccache"))
535 '#+extensions)
536 %load-compiled-path))
537
538 ;; Load the compiler modules upfront.
539 (compile #f)
540
541 (mkdir #$output)
542 (chdir #+module-tree)
543 (process-directory "." #$output))))
544
545 (computed-file name build
546 #:guile guile-for-build
547 #:options
548 `(#:local-build? #f ;allow substitutes
549
550 ;; Don't annoy people about _IONBF deprecation.
551 #:env-vars (("GUILE_WARN_DEPRECATED" . "no")))))
552
553
554;;;
555;;; Building.
556;;;
557
558(define (guile-for-build version)
559 "Return a derivation for Guile 2.0 or 2.2, whichever matches the currently
560running Guile."
561 (define canonical-package ;soft reference
562 (module-ref (resolve-interface '(gnu packages base))
563 'canonical-package))
564
565 (match version
566 ("2.2.2"
567 ;; Gross hack to avoid ABI incompatibilities (see
568 ;; <https://bugs.gnu.org/29570>.)
569 (module-ref (resolve-interface '(gnu packages guile))
570 'guile-2.2.2))
571 ("2.2"
572 (canonical-package (module-ref (resolve-interface '(gnu packages guile))
573 'guile-2.2/fixed)))
574 ("2.0"
575 (canonical-package (specification->package "guile@2.0")))))
576
577(define* (guix-derivation source version
578 #:optional (guile-version (effective-version)))
579 "Return, as a monadic value, the derivation to build the Guix from SOURCE
580for GUILE-VERSION. Use VERSION as the version string."
581 (define (shorten version)
582 (if (and (string-every char-set:hex-digit version)
583 (> (string-length version) 9))
584 (string-take version 9) ;Git commit
585 version))
586
587 (define guile
588 (guile-for-build guile-version))
589
590 (mbegin %store-monad
591 (set-guile-for-build guile)
592 (lower-object (compiled-guix source
593 #:version version
594 #:name (string-append "guix-"
595 (shorten version))
596 #:guile-version (match guile-version
597 ("2.2.2" "2.2")
598 (version version))
599 #:guile-for-build guile))))