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