diff options
| author | Ludovic Courtès <ludo@gnu.org> | 2017-05-03 23:03:20 +0200 |
|---|---|---|
| committer | Ludovic Courtès <ludo@gnu.org> | 2017-05-03 23:50:15 +0200 |
| commit | cd903ef7871170d3c4eced45418459d293ef48a7 (patch) | |
| tree | 1a3bc718ba57704583a63595732220f31f5c0cf0 | |
| parent | 1dc0a66591f4fb37fc0df2aec6d3b0b7e2046c70 (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.am | 2 | ||||
| -rw-r--r-- | gnu/packages.scm | 93 | ||||
| -rw-r--r-- | gnu/tests.scm | 2 | ||||
| -rw-r--r-- | guix/discovery.scm | 131 | ||||
| -rw-r--r-- | tests/discovery.scm | 52 |
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 | ||
| 145 | returned 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. | ||
| 177 | Optionally, 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 | ||
| 197 | search." | ||
| 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 |
| 210 | the initial value of RESULT. It is guaranteed to never traverse the | 143 | the initial value of RESULT. It is guaranteed to never traverse the |
| 211 | same package twice." | 144 | same 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 | ||
| 40 | returned 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. | ||
| 73 | Optionally, 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. | ||
| 93 | Call (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 | ||
| 106 | search. Entries in PATH can be directory names (strings) or (DIRECTORY | ||
| 107 | . SUB-DIRECTORY) pairs, in which case modules are searched for beneath | ||
| 108 | SUB-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, | ||
| 113 | using INIT as the initial value of RESULT. It is guaranteed to never traverse | ||
| 114 | the 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") | ||
