Add (guix modules).
[jackhill/guix/guix.git] / guix / modules.scm
CommitLineData
7ca87354
LC
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2016 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 modules)
20 #:use-module ((guix utils) #:select (memoize))
21 #:use-module (guix sets)
22 #:use-module (srfi srfi-26)
23 #:use-module (ice-9 match)
24 #:export (source-module-closure
25 live-module-closure
26 guix-module-name?))
27
28;;; Commentary:
29;;;
30;;; This module provides introspection tools for Guile modules at the source
31;;; level. Namely, it allows you to determine the closure of a module; it
32;;; does so just by reading the 'define-module' clause of the module and its
33;;; dependencies. This is primarily useful as an argument to
34;;; 'with-imported-modules'.
35;;;
36;;; Code:
37
38(define (colon-symbol? obj)
39 "Return true if OBJ is a symbol that starts with a colon."
40 (and (symbol? obj)
41 (string-prefix? ":" (symbol->string obj))))
42
43(define (colon-symbol->keyword symbol)
44 "Convert SYMBOL to a keyword after stripping its initial ':'."
45 (symbol->keyword
46 (string->symbol (string-drop (symbol->string symbol) 1))))
47
48(define (extract-dependencies clauses)
49 "Return the list of modules imported according to the given 'define-module'
50CLAUSES."
51 (let loop ((clauses clauses)
52 (result '()))
53 (match clauses
54 (()
55 (reverse result))
56 ((#:use-module (module (or #:select #:hide #:prefix #:renamer) _)
57 rest ...)
58 (loop rest (cons module result)))
59 ((#:use-module module rest ...)
60 (loop rest (cons module result)))
61 ((#:autoload module _ rest ...)
62 (loop rest (cons module result)))
63 (((or #:export #:re-export #:export-syntax #:re-export-syntax
64 #:replace #:version)
65 _ rest ...)
66 (loop rest result))
67 (((or #:pure #:no-backtrace) rest ...)
68 (loop rest result))
69 (((? colon-symbol? symbol) rest ...)
70 (loop (cons (colon-symbol->keyword symbol) rest)
71 result)))))
72
73(define module-file-dependencies
74 (memoize
75 (lambda (file)
76 "Return the list of the names of modules that the Guile module in FILE
77depends on."
78 (call-with-input-file file
79 (lambda (port)
80 (match (read port)
81 (('define-module name clauses ...)
82 (extract-dependencies clauses))
83 ;; XXX: R6RS 'library' form is ignored.
84 (_
85 '())))))))
86
87(define (module-name->file-name module)
88 "Return the file name for MODULE."
89 (string-append (string-join (map symbol->string module) "/")
90 ".scm"))
91
92(define (guix-module-name? name)
93 "Return true if NAME (a list of symbols) denotes a Guix or GuixSD module."
94 (match name
95 (('guix _ ...) #t)
96 (('gnu _ ...) #t)
97 (_ #f)))
98
99(define* (source-module-dependencies module #:optional (load-path %load-path))
100 "Return the modules used by MODULE by looking at its source code."
101 ;; The (system syntax) module is a special-case because it has no
102 ;; corresponding source file (as of Guile 2.0.)
103 (if (equal? module '(system syntax))
104 '()
105 (module-file-dependencies
106 (search-path load-path
107 (module-name->file-name module)))))
108
109(define* (module-closure modules
110 #:key
111 (select? guix-module-name?)
112 (dependencies source-module-dependencies))
113 "Return the closure of MODULES, calling DEPENDENCIES to determine the list
114of modules used by a given module. MODULES and the result are a list of Guile
115module names. Only modules that match SELECT? are considered."
116 (let loop ((modules modules)
117 (result '())
118 (visited (set)))
119 (match modules
120 (()
121 (reverse result))
122 ((module rest ...)
123 (cond ((set-contains? visited module)
124 (loop rest result visited))
125 ((select? module)
126 (loop (append (dependencies module) rest)
127 (cons module result)
128 (set-insert module visited)))
129 (else
130 (loop rest result visited)))))))
131
132(define* (source-module-closure modules
133 #:optional (load-path %load-path)
134 #:key (select? guix-module-name?))
135 "Return the closure of MODULES by reading 'define-module' forms in their
136source code. MODULES and the result are a list of Guile module names. Only
137modules that match SELECT? are considered."
138 (module-closure modules
139 #:dependencies (cut source-module-dependencies <> load-path)
140 #:select? select?))
141
142(define* (live-module-closure modules
143 #:key (select? guix-module-name?))
144 "Return the closure of MODULES, determined by looking at live (loaded)
145module information. MODULES and the result are a list of Guile module names.
146Only modules that match SELECT? are considered."
147 (define (dependencies module)
148 (map module-name
149 (delq the-scm-module (module-uses (resolve-module module)))))
150
151 (module-closure modules
152 #:dependencies dependencies
153 #:select? select?))
154
155;;; modules.scm ends here