diff options
author | Andy Wingo <wingo@igalia.com> | 2017-04-13 11:04:42 +0200 |
---|---|---|
committer | Andy Wingo <wingo@igalia.com> | 2017-04-28 13:49:31 +0200 |
commit | e004f731b12e91584f9260b35d2a5fa158eb7a16 (patch) | |
tree | ef2a2910d694b9445e97855bdbfdb17f6271e6d1 /gnu | |
parent | 17815161ce62fa228acbc3604e06d7026f7f29fd (diff) | |
download | guix-e004f731b12e91584f9260b35d2a5fa158eb7a16.tar.gz |
gnu: Add find-package-binding.
* gnu/packages.scm (find-package-binding): New export.
Diffstat (limited to 'gnu')
-rw-r--r-- | gnu/packages.scm | 62 |
1 files changed, 61 insertions, 1 deletions
diff --git a/gnu/packages.scm b/gnu/packages.scm index 92bab7228a..5e85d3dd68 100644 --- a/gnu/packages.scm +++ b/gnu/packages.scm @@ -55,7 +55,9 @@ find-newest-available-packages specification->package - specification->package+output)) + specification->package+output + + find-package-binding)) ;;; Commentary: ;;; @@ -368,3 +370,61 @@ version; if SPEC does not specify an output, return OUTPUT." (leave (_ "package `~a' lacks output `~a'~%") (package-full-name package) sub-drv)))))) + +(define (find-package-binding package) + "Find the module that exports PACKAGE. Return two values, an interface name +and a symbol that can be used to import PACKAGE. Signal an error if no public variable binds PACKAGE." + (define (strip-extension file exts) + (or (or-map (lambda (ext) + (and (string-suffix? ext file) + (substring file 0 (- (string-length file) + (string-length ext))))) + exts) + file)) + (define (file-name->module-name file) + (and (not (absolute-file-name? file)) + (map string->symbol + (string-split (strip-extension file %load-extensions) + #\/)))) + ;; Instead of building a table and always doing a search, first just see if + ;; we can use the package's location to find its module and look in that + ;; module. + (define (global-search) + (let search ((modules (all-package-modules))) + (match modules + (() + (raise (condition + (&message (message + (format #f (_ "~a@~a: binding not found") + (package-name package) + (package-version package))))))) + ((mod . modules) + (let ((next (lambda () (search modules)))) + (local-search (module-name mod) mod next)))))) + (define (local-search module-name iface k) + (let lp ((bindings (module-map cons iface))) + (match bindings + (() (k)) + (((sym . var) . bindings) + (if (eq? (variable-ref var) package) + (values module-name sym) + (lp bindings)))))) + (cond + ((package-location package) + => (lambda (loc) + (cond + ((file-name->module-name (location-file loc)) + => (lambda (module-name) + (cond + ((false-if-exception (resolve-interface module-name)) + => (lambda (iface) + (let ((def (string->symbol (package-name package)))) + (cond + ((and (module-variable iface def) + (eq? (module-ref iface def) package)) + (values module-name def)) + (else + (local-search module-name iface global-search)))))) + (else (global-search))))) + (else (global-search))))) + (else (global-search)))) |