scan-api.scm 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. ;;; scan-api --- Scan and group interpreter and libguile interface elements
  2. ;; Copyright (C) 2002, 2006, 2011 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 %include-in-guild-list #f)
  62. (define %summary "Generate an API description for a Guile extension.")
  63. (define put set-object-property!)
  64. (define get object-property)
  65. (define (add-props object . args)
  66. (let loop ((args args))
  67. (if (null? args)
  68. object ; retval
  69. (let ((key (car args))
  70. (value (cadr args)))
  71. (put object key value)
  72. (loop (cddr args))))))
  73. (define (scan re command match)
  74. (let ((rx (make-regexp re))
  75. (port (open-pipe command OPEN_READ)))
  76. (let loop ((line (read-line port)))
  77. (or (eof-object? line)
  78. (begin
  79. (cond ((regexp-exec rx line) => match))
  80. (loop (read-line port)))))))
  81. (define (scan-Scheme! ht guile)
  82. (scan "^.guile.+: ([^ \t]+)([ \t]+(.+))*$"
  83. (format #f "~A -c '~S ~S'"
  84. guile
  85. '(use-modules (ice-9 session))
  86. '(apropos "."))
  87. (lambda (m)
  88. (let ((x (string->symbol (match:substring m 1))))
  89. (put x 'Scheme (or (match:substring m 3)
  90. ""))
  91. (hashq-set! ht x #t)))))
  92. (define (scan-C! ht sofile)
  93. (scan "^[0-9a-fA-F]+ ([B-TV-Z]) (.+)$"
  94. (format #f "nm ~A" sofile)
  95. (lambda (m)
  96. (let ((x (string->symbol (match:substring m 2))))
  97. (put x 'C (string->symbol (match:substring m 1)))
  98. (and (hashq-get-handle ht x)
  99. (error "both Scheme and C:" x))
  100. (hashq-set! ht x #t)))))
  101. (define THIS-MODULE (current-module))
  102. (define (in-group? x group)
  103. (memq group (get x 'groups)))
  104. (define (name-prefix? x prefix)
  105. (string-match (string-append "^" prefix) (symbol->string x)))
  106. (define (add-group-name! x name)
  107. (put x 'groups (cons name (get x 'groups))))
  108. (define (make-grok-proc name form)
  109. (let* ((predicate? (eval form THIS-MODULE))
  110. (p (lambda (x)
  111. (and (predicate? x)
  112. (add-group-name! x name)))))
  113. (put p 'name name)
  114. p))
  115. (define (make-members-proc name members)
  116. (let ((p (lambda (x)
  117. (and (memq x members)
  118. (add-group-name! x name)))))
  119. (put p 'name name)
  120. p))
  121. (define (make-grouper files) ; \/^^^o/ . o
  122. (let ((hook (make-hook 1))) ; /\____\
  123. (for-each
  124. (lambda (file)
  125. (for-each
  126. (lambda (gdef)
  127. (let ((name (car gdef))
  128. (members (assq-ref gdef 'members))
  129. (grok (assq-ref gdef 'grok)))
  130. (or members grok
  131. (error "bad grouping, must have `members' or `grok'"))
  132. (add-hook! hook
  133. (if grok
  134. (add-props (make-grok-proc name (cadr grok))
  135. 'description
  136. (assq-ref gdef 'description))
  137. (make-members-proc name members))
  138. #t))) ; append
  139. (read (open-file file OPEN_READ))))
  140. files)
  141. hook))
  142. (define (scan-api . args)
  143. (let ((guile (list-ref args 0))
  144. (sofile (list-ref args 1))
  145. (grouper (false-if-exception (make-grouper (cddr args))))
  146. (ht (make-hash-table 3331)))
  147. (scan-Scheme! ht guile)
  148. (scan-C! ht sofile)
  149. (let ((all (sort (hash-fold (lambda (key value prior-result)
  150. (add-props
  151. key
  152. 'string (symbol->string key)
  153. 'scan-data (or (get key 'Scheme)
  154. (get key 'C))
  155. 'groups (if (get key 'Scheme)
  156. '(Scheme)
  157. '(C)))
  158. (and grouper (run-hook grouper key))
  159. (cons key prior-result))
  160. '()
  161. ht)
  162. (lambda (a b)
  163. (string<? (get a 'string)
  164. (get b 'string))))))
  165. (format #t ";;; generated by scan-api -- do not edit!\n\n")
  166. (format #t "(\n")
  167. (format #t "(meta\n")
  168. (format #t " (GUILE_LOAD_PATH . ~S)\n"
  169. (or (getenv "GUILE_LOAD_PATH") ""))
  170. (format #t " (LTDL_LIBRARY_PATH . ~S)\n"
  171. (or (getenv "LTDL_LIBRARY_PATH") ""))
  172. (format #t " (guile . ~S)\n" guile)
  173. (format #t " (libguileinterface . ~S)\n"
  174. (let ((i #f))
  175. (scan "(.+)"
  176. (format #f "~A -c '(display ~A)'"
  177. guile
  178. '(assq-ref %guile-build-info
  179. 'libguileinterface))
  180. (lambda (m) (set! i (match:substring m 1))))
  181. i))
  182. (format #t " (sofile . ~S)\n" sofile)
  183. (format #t " ~A\n"
  184. (cons 'groups (append (if grouper
  185. (map (lambda (p) (get p 'name))
  186. (hook->list grouper))
  187. '())
  188. '(Scheme C))))
  189. (format #t ") ;; end of meta\n")
  190. (format #t "(interface\n")
  191. (for-each (lambda (x)
  192. (format #t "(~A ~A (scan-data ~S))\n"
  193. x
  194. (cons 'groups (get x 'groups))
  195. (get x 'scan-data)))
  196. all)
  197. (format #t ") ;; end of interface\n")
  198. (format #t ") ;; eof\n")))
  199. #t)
  200. (define main scan-api)
  201. ;;; scan-api ends here