scan-api.scm 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. ;;; scan-api --- Scan and group interpreter and libguile interface elements
  2. ;; Copyright (C) 2002, 2006 Free Software Foundation, Inc.
  3. ;;
  4. ;; This program is free software; you can redistribute it and/or
  5. ;; modify it under the terms of the GNU Lesser General Public License
  6. ;; as published by the Free Software Foundation; either version 3, or
  7. ;; (at your option) any later version.
  8. ;;
  9. ;; This program is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. ;; Lesser General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU Lesser General Public
  15. ;; License along with this software; see the file COPYING.LESSER. If
  16. ;; not, write to the Free Software Foundation, Inc., 51 Franklin
  17. ;; Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. ;;; Author: Thien-Thi Nguyen <ttn@gnu.org>
  19. ;;; Commentary:
  20. ;; Usage: scan-api GUILE SOFILE [GROUPINGS ...]
  21. ;;
  22. ;; Invoke GUILE, an executable guile interpreter, and use nm(1) on SOFILE, a
  23. ;; shared-object library, to determine available interface elements, and
  24. ;; display them to stdout as an alist:
  25. ;;
  26. ;; ((meta ...) (interface ...))
  27. ;;
  28. ;; The meta fields are `GUILE_LOAD_PATH', `LTDL_LIBRARY_PATH', `guile'
  29. ;; `libguileinterface', `sofile' and `groups'. The interface elements are in
  30. ;; turn sub-alists w/ keys `groups' and `scan-data'. Interface elements
  31. ;; initially belong in one of two groups `Scheme' or `C' (but not both --
  32. ;; signal error if that happens).
  33. ;;
  34. ;; Optional GROUPINGS ... are files each containing a single "grouping
  35. ;; definition" alist with each entry of the form:
  36. ;;
  37. ;; (NAME (description "DESCRIPTION") (members SYM...))
  38. ;;
  39. ;; All of the SYM... should be proper subsets of the interface. In addition
  40. ;; to `description' and `members' forms, the entry may optionally include:
  41. ;;
  42. ;; (grok USE-MODULES (lambda (x) CODE))
  43. ;;
  44. ;; where CODE implements a group-membership predicate to be applied to `x', a
  45. ;; symbol. [When evaluated, CODE can assume (use-modules MODULE) has been
  46. ;; executed where MODULE is an element of USE-MODULES, a list. [NOT YET
  47. ;; IMPLEMENTED!]]
  48. ;;
  49. ;; Currently, there are two convenience predicates that operate on `x':
  50. ;; (in-group? x GROUP)
  51. ;; (name-prefix? x PREFIX)
  52. ;;
  53. ;; TODO: Allow for concurrent Scheme/C membership.
  54. ;; Completely separate reporting.
  55. ;;; Code:
  56. (define-module (scripts scan-api)
  57. :use-module (ice-9 popen)
  58. :use-module (ice-9 rdelim)
  59. :use-module (ice-9 regex)
  60. :export (scan-api))
  61. (define put set-object-property!)
  62. (define get object-property)
  63. (define (add-props object . args)
  64. (let loop ((args args))
  65. (if (null? args)
  66. object ; retval
  67. (let ((key (car args))
  68. (value (cadr args)))
  69. (put object key value)
  70. (loop (cddr args))))))
  71. (define (scan re command match)
  72. (let ((rx (make-regexp re))
  73. (port (open-pipe command OPEN_READ)))
  74. (let loop ((line (read-line port)))
  75. (or (eof-object? line)
  76. (begin
  77. (cond ((regexp-exec rx line) => match))
  78. (loop (read-line port)))))))
  79. (define (scan-Scheme! ht guile)
  80. (scan "^.guile.+: ([^ \t]+)([ \t]+(.+))*$"
  81. (format #f "~A -c '~S ~S'"
  82. guile
  83. '(use-modules (ice-9 session))
  84. '(apropos "."))
  85. (lambda (m)
  86. (let ((x (string->symbol (match:substring m 1))))
  87. (put x 'Scheme (or (match:substring m 3)
  88. ""))
  89. (hashq-set! ht x #t)))))
  90. (define (scan-C! ht sofile)
  91. (scan "^[0-9a-fA-F]+ ([B-TV-Z]) (.+)$"
  92. (format #f "nm ~A" sofile)
  93. (lambda (m)
  94. (let ((x (string->symbol (match:substring m 2))))
  95. (put x 'C (string->symbol (match:substring m 1)))
  96. (and (hashq-get-handle ht x)
  97. (error "both Scheme and C:" x))
  98. (hashq-set! ht x #t)))))
  99. (define THIS-MODULE (current-module))
  100. (define (in-group? x group)
  101. (memq group (get x 'groups)))
  102. (define (name-prefix? x prefix)
  103. (string-match (string-append "^" prefix) (symbol->string x)))
  104. (define (add-group-name! x name)
  105. (put x 'groups (cons name (get x 'groups))))
  106. (define (make-grok-proc name form)
  107. (let* ((predicate? (eval form THIS-MODULE))
  108. (p (lambda (x)
  109. (and (predicate? x)
  110. (add-group-name! x name)))))
  111. (put p 'name name)
  112. p))
  113. (define (make-members-proc name members)
  114. (let ((p (lambda (x)
  115. (and (memq x members)
  116. (add-group-name! x name)))))
  117. (put p 'name name)
  118. p))
  119. (define (make-grouper files) ; \/^^^o/ . o
  120. (let ((hook (make-hook 1))) ; /\____\
  121. (for-each
  122. (lambda (file)
  123. (for-each
  124. (lambda (gdef)
  125. (let ((name (car gdef))
  126. (members (assq-ref gdef 'members))
  127. (grok (assq-ref gdef 'grok)))
  128. (or members grok
  129. (error "bad grouping, must have `members' or `grok'"))
  130. (add-hook! hook
  131. (if grok
  132. (add-props (make-grok-proc name (cadr grok))
  133. 'description
  134. (assq-ref gdef 'description))
  135. (make-members-proc name members))
  136. #t))) ; append
  137. (read (open-file file OPEN_READ))))
  138. files)
  139. hook))
  140. (define (scan-api . args)
  141. (let ((guile (list-ref args 0))
  142. (sofile (list-ref args 1))
  143. (grouper (false-if-exception (make-grouper (cddr args))))
  144. (ht (make-hash-table 3331)))
  145. (scan-Scheme! ht guile)
  146. (scan-C! ht sofile)
  147. (let ((all (sort (hash-fold (lambda (key value prior-result)
  148. (add-props
  149. key
  150. 'string (symbol->string key)
  151. 'scan-data (or (get key 'Scheme)
  152. (get key 'C))
  153. 'groups (if (get key 'Scheme)
  154. '(Scheme)
  155. '(C)))
  156. (and grouper (run-hook grouper key))
  157. (cons key prior-result))
  158. '()
  159. ht)
  160. (lambda (a b)
  161. (string<? (get a 'string)
  162. (get b 'string))))))
  163. (format #t ";;; generated by scan-api -- do not edit!\n\n")
  164. (format #t "(\n")
  165. (format #t "(meta\n")
  166. (format #t " (GUILE_LOAD_PATH . ~S)\n"
  167. (or (getenv "GUILE_LOAD_PATH") ""))
  168. (format #t " (LTDL_LIBRARY_PATH . ~S)\n"
  169. (or (getenv "LTDL_LIBRARY_PATH") ""))
  170. (format #t " (guile . ~S)\n" guile)
  171. (format #t " (libguileinterface . ~S)\n"
  172. (let ((i #f))
  173. (scan "(.+)"
  174. (format #f "~A -c '(display ~A)'"
  175. guile
  176. '(assq-ref %guile-build-info
  177. 'libguileinterface))
  178. (lambda (m) (set! i (match:substring m 1))))
  179. i))
  180. (format #t " (sofile . ~S)\n" sofile)
  181. (format #t " ~A\n"
  182. (cons 'groups (append (if grouper
  183. (map (lambda (p) (get p 'name))
  184. (hook->list grouper))
  185. '())
  186. '(Scheme C))))
  187. (format #t ") ;; end of meta\n")
  188. (format #t "(interface\n")
  189. (for-each (lambda (x)
  190. (format #t "(~A ~A (scan-data ~S))\n"
  191. x
  192. (cons 'groups (get x 'groups))
  193. (get x 'scan-data)))
  194. all)
  195. (format #t ") ;; end of interface\n")
  196. (format #t ") ;; eof\n")))
  197. #t)
  198. (define main scan-api)
  199. ;;; scan-api ends here