summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2017-05-03 23:03:20 +0200
committerLudovic Courtès <ludo@gnu.org>2017-05-03 23:50:15 +0200
commitcd903ef7871170d3c4eced45418459d293ef48a7 (patch)
tree1a3bc718ba57704583a63595732220f31f5c0cf0
parent1dc0a66591f4fb37fc0df2aec6d3b0b7e2046c70 (diff)
Add (guix discovery).
* guix/discovery.scm, tests/discovery.scm: New files. * gnu/packages.scm (scheme-files, file-name->module-name) (scheme-modules, all-package-modules): Remove. (fold-packages): Rewrite in terms of 'fold-module-public-variables'. * gnu/tests.scm: Use (guix discovery). * Makefile.am (MODULES): Add guix/discovery.scm. (SCM_TESTS): Add tests/discovery.scm.
-rw-r--r--Makefile.am2
-rw-r--r--gnu/packages.scm93
-rw-r--r--gnu/tests.scm2
-rw-r--r--guix/discovery.scm131
-rw-r--r--tests/discovery.scm52
5 files changed, 194 insertions, 86 deletions
diff --git a/Makefile.am b/Makefile.am
index 426f8327d9c..c6d8de68bc0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -50,6 +50,7 @@ MODULES = \
50 guix/sets.scm \ 50 guix/sets.scm \
51 guix/modules.scm \ 51 guix/modules.scm \
52 guix/download.scm \ 52 guix/download.scm \
53 guix/discovery.scm \
53 guix/git-download.scm \ 54 guix/git-download.scm \
54 guix/hg-download.scm \ 55 guix/hg-download.scm \
55 guix/monads.scm \ 56 guix/monads.scm \
@@ -279,6 +280,7 @@ SCM_TESTS = \
279 tests/records.scm \ 280 tests/records.scm \
280 tests/upstream.scm \ 281 tests/upstream.scm \
281 tests/combinators.scm \ 282 tests/combinators.scm \
283 tests/discovery.scm \
282 tests/utils.scm \ 284 tests/utils.scm \
283 tests/build-utils.scm \ 285 tests/build-utils.scm \
284 tests/packages.scm \ 286 tests/packages.scm \
diff --git a/gnu/packages.scm b/gnu/packages.scm
index 08f1340612d..57907155fb4 100644
--- a/gnu/packages.scm
+++ b/gnu/packages.scm
@@ -24,12 +24,11 @@
24 #:use-module (guix packages) 24 #:use-module (guix packages)
25 #:use-module (guix ui) 25 #:use-module (guix ui)
26 #:use-module (guix utils) 26 #:use-module (guix utils)
27 #:use-module (guix discovery)
27 #:use-module (guix memoization) 28 #:use-module (guix memoization)
28 #:use-module (guix combinators)
29 #:use-module ((guix build utils) 29 #:use-module ((guix build utils)
30 #:select ((package-name->name+version 30 #:select ((package-name->name+version
31 . hyphen-separated-name->name+version))) 31 . hyphen-separated-name->name+version)))
32 #:use-module (ice-9 ftw)
33 #:use-module (ice-9 vlist) 32 #:use-module (ice-9 vlist)
34 #:use-module (ice-9 match) 33 #:use-module (ice-9 match)
35 #:use-module (srfi srfi-1) 34 #:use-module (srfi srfi-1)
@@ -48,7 +47,6 @@
48 %package-module-path 47 %package-module-path
49 48
50 fold-packages 49 fold-packages
51 scheme-modules ;XXX: for lack of a better place
52 50
53 find-packages-by-name 51 find-packages-by-name
54 find-best-packages-by-name 52 find-best-packages-by-name
@@ -140,92 +138,17 @@ for system '~a'")
140 directory)) 138 directory))
141 %load-path))) 139 %load-path)))
142 140
143(define* (scheme-files directory)
144 "Return the list of Scheme files found under DIRECTORY, recursively. The
145returned list is sorted in alphabetical order."
146
147 ;; Sort entries so that 'fold-packages' works in a deterministic fashion
148 ;; regardless of details of the underlying file system.
149 (sort (file-system-fold (const #t) ; enter?
150 (lambda (path stat result) ; leaf
151 (if (string-suffix? ".scm" path)
152 (cons path result)
153 result))
154 (lambda (path stat result) ; down
155 result)
156 (lambda (path stat result) ; up
157 result)
158 (const #f) ; skip
159 (lambda (path stat errno result)
160 (warning (G_ "cannot access `~a': ~a~%")
161 path (strerror errno))
162 result)
163 '()
164 directory
165 stat)
166 string<?))
167
168(define file-name->module-name
169 (let ((not-slash (char-set-complement (char-set #\/))))
170 (lambda (file)
171 "Return the module name (a list of symbols) corresponding to FILE."
172 (map string->symbol
173 (string-tokenize (string-drop-right file 4) not-slash)))))
174
175(define* (scheme-modules directory #:optional sub-directory)
176 "Return the list of Scheme modules available under DIRECTORY.
177Optionally, narrow the search to SUB-DIRECTORY."
178 (define prefix-len
179 (string-length directory))
180
181 (filter-map (lambda (file)
182 (let* ((file (substring file prefix-len))
183 (module (file-name->module-name file)))
184 (catch #t
185 (lambda ()
186 (resolve-interface module))
187 (lambda args
188 ;; Report the error, but keep going.
189 (warn-about-load-error module args)
190 #f))))
191 (scheme-files (if sub-directory
192 (string-append directory "/" sub-directory)
193 directory))))
194
195(define* (all-package-modules #:optional (path (%package-module-path)))
196 "Return the list of package modules found in PATH, a list of directories to
197search."
198 (fold-right (lambda (spec result)
199 (match spec
200 ((? string? directory)
201 (append (scheme-modules directory) result))
202 ((directory . sub-directory)
203 (append (scheme-modules directory sub-directory)
204 result))))
205 '()
206 path))
207
208(define (fold-packages proc init) 141(define (fold-packages proc init)
209 "Call (PROC PACKAGE RESULT) for each available package, using INIT as 142 "Call (PROC PACKAGE RESULT) for each available package, using INIT as
210the initial value of RESULT. It is guaranteed to never traverse the 143the initial value of RESULT. It is guaranteed to never traverse the
211same package twice." 144same package twice."
212 (identity ; discard second return value 145 (fold-module-public-variables (lambda (object result)
213 (fold2 (lambda (module result seen) 146 (if (and (package? object)
214 (fold2 (lambda (var result seen) 147 (not (hidden-package? object)))
215 (if (and (package? var) 148 (proc object result)
216 (not (vhash-assq var seen)) 149 result))
217 (not (hidden-package? var))) 150 init
218 (values (proc var result) 151 (all-modules (%package-module-path))))
219 (vhash-consq var #t seen))
220 (values result seen)))
221 result
222 seen
223 (module-map (lambda (sym var)
224 (false-if-exception (variable-ref var)))
225 module)))
226 init
227 vlist-null
228 (all-package-modules))))
229 152
230(define find-packages-by-name 153(define find-packages-by-name
231 (let ((packages (delay 154 (let ((packages (delay
diff --git a/gnu/tests.scm b/gnu/tests.scm
index e84d1ebb209..0df6e5a2ef7 100644
--- a/gnu/tests.scm
+++ b/gnu/tests.scm
@@ -27,7 +27,7 @@
27 #:use-module (gnu services) 27 #:use-module (gnu services)
28 #:use-module (gnu services base) 28 #:use-module (gnu services base)
29 #:use-module (gnu services shepherd) 29 #:use-module (gnu services shepherd)
30 #:use-module ((gnu packages) #:select (scheme-modules)) 30 #:use-module ((guix discovery) #:select (scheme-modules))
31 #:use-module (srfi srfi-1) 31 #:use-module (srfi srfi-1)
32 #:use-module (srfi srfi-9 gnu) 32 #:use-module (srfi srfi-9 gnu)
33 #:use-module (ice-9 match) 33 #:use-module (ice-9 match)
diff --git a/guix/discovery.scm b/guix/discovery.scm
new file mode 100644
index 00000000000..319ba7c872c
--- /dev/null
+++ b/guix/discovery.scm
@@ -0,0 +1,131 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2012, 2013, 2014, 2015, 2016, 2017 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 discovery)
20 #:use-module (guix ui)
21 #:use-module (guix combinators)
22 #:use-module (srfi srfi-1)
23 #:use-module (ice-9 match)
24 #:use-module (ice-9 vlist)
25 #:use-module (ice-9 ftw)
26 #:export (scheme-modules
27 fold-modules
28 all-modules
29 fold-module-public-variables))
30
31;;; Commentary:
32;;;
33;;; This module provides tools to discover Guile modules and the variables
34;;; they export.
35;;;
36;;; Code:
37
38(define* (scheme-files directory)
39 "Return the list of Scheme files found under DIRECTORY, recursively. The
40returned list is sorted in alphabetical order."
41
42 ;; Sort entries so that 'fold-packages' works in a deterministic fashion
43 ;; regardless of details of the underlying file system.
44 (sort (file-system-fold (const #t) ;enter?
45 (lambda (path stat result) ;leaf
46 (if (string-suffix? ".scm" path)
47 (cons path result)
48 result))
49 (lambda (path stat result) ;down
50 result)
51 (lambda (path stat result) ;up
52 result)
53 (const #f) ;skip
54 (lambda (path stat errno result)
55 (unless (= ENOENT errno)
56 (warning (G_ "cannot access `~a': ~a~%")
57 path (strerror errno)))
58 result)
59 '()
60 directory
61 stat)
62 string<?))
63
64(define file-name->module-name
65 (let ((not-slash (char-set-complement (char-set #\/))))
66 (lambda (file)
67 "Return the module name (a list of symbols) corresponding to FILE."
68 (map string->symbol
69 (string-tokenize (string-drop-right file 4) not-slash)))))
70
71(define* (scheme-modules directory #:optional sub-directory)
72 "Return the list of Scheme modules available under DIRECTORY.
73Optionally, narrow the search to SUB-DIRECTORY."
74 (define prefix-len
75 (string-length directory))
76
77 (filter-map (lambda (file)
78 (let* ((file (substring file prefix-len))
79 (module (file-name->module-name file)))
80 (catch #t
81 (lambda ()
82 (resolve-interface module))
83 (lambda args
84 ;; Report the error, but keep going.
85 (warn-about-load-error module args)
86 #f))))
87 (scheme-files (if sub-directory
88 (string-append directory "/" sub-directory)
89 directory))))
90
91(define (fold-modules proc init path)
92 "Fold over all the Scheme modules present in PATH, a list of directories.
93Call (PROC MODULE RESULT) for each module that is found."
94 (fold (lambda (spec result)
95 (match spec
96 ((? string? directory)
97 (fold proc result (scheme-modules directory)))
98 ((directory . sub-directory)
99 (fold proc result
100 (scheme-modules directory sub-directory)))))
101 '()
102 path))
103
104(define (all-modules path)
105 "Return the list of package modules found in PATH, a list of directories to
106search. Entries in PATH can be directory names (strings) or (DIRECTORY
107. SUB-DIRECTORY) pairs, in which case modules are searched for beneath
108SUB-DIRECTORY."
109 (fold-modules cons '() path))
110
111(define (fold-module-public-variables proc init modules)
112 "Call (PROC OBJECT RESULT) for each variable exported by one of MODULES,
113using INIT as the initial value of RESULT. It is guaranteed to never traverse
114the same object twice."
115 (identity ; discard second return value
116 (fold2 (lambda (module result seen)
117 (fold2 (lambda (var result seen)
118 (if (not (vhash-assq var seen))
119 (values (proc var result)
120 (vhash-consq var #t seen))
121 (values result seen)))
122 result
123 seen
124 (module-map (lambda (sym var)
125 (false-if-exception (variable-ref var)))
126 module)))
127 init
128 vlist-null
129 modules)))
130
131;;; discovery.scm ends here
diff --git a/tests/discovery.scm b/tests/discovery.scm
new file mode 100644
index 00000000000..b838731e16f
--- /dev/null
+++ b/tests/discovery.scm
@@ -0,0 +1,52 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2017 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 (test-discovery)
20 #:use-module (guix discovery)
21 #:use-module (guix build-system)
22 #:use-module (srfi srfi-64)
23 #:use-module (ice-9 match))
24
25(define %top-srcdir
26 (dirname (search-path %load-path "guix.scm")))
27
28(test-begin "discovery")
29
30(test-assert "scheme-modules"
31 (match (map module-name (scheme-modules %top-srcdir "guix/import"))
32 ((('guix 'import _ ...) ..1)
33 #t)))
34
35(test-assert "all-modules"
36 (match (map module-name
37 (all-modules `((,%top-srcdir . "guix/build-system"))))
38 ((('guix 'build-system names) ..1)
39 names)))
40
41(test-assert "fold-module-public-variables"
42 (let ((modules (all-modules `((,%top-srcdir . "guix/build-system")))))
43 (match (fold-module-public-variables (lambda (obj result)
44 (if (build-system? obj)
45 (cons obj result)
46 result))
47 '()
48 modules)
49 (((? build-system? bs) ..1)
50 bs))))
51
52(test-end "discovery")