cus-dep.el 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. ;;; cus-dep.el --- find customization dependencies
  2. ;;
  3. ;; Copyright (C) 1997, 2001-2017 Free Software Foundation, Inc.
  4. ;;
  5. ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>
  6. ;; Keywords: internal
  7. ;; Package: emacs
  8. ;; This file is part of GNU Emacs.
  9. ;; GNU Emacs is free software: you can redistribute it and/or modify
  10. ;; it under the terms of the GNU General Public License as published by
  11. ;; the Free Software Foundation, either version 3 of the License, or
  12. ;; (at your option) any later version.
  13. ;; GNU Emacs is distributed in the hope that it will be useful,
  14. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. ;; GNU General Public License for more details.
  17. ;; You should have received a copy of the GNU General Public License
  18. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  19. ;;; Commentary:
  20. ;;; Code:
  21. (require 'widget)
  22. (require 'cus-face)
  23. (defvar generated-custom-dependencies-file "cus-load.el"
  24. "Output file for `custom-make-dependencies'.")
  25. ;; See finder-no-scan-regexp in finder.el.
  26. (defvar custom-dependencies-no-scan-regexp "\\(^\\.#\\|\\(loaddefs\\|\
  27. ldefs-boot\\|cus-load\\|finder-inf\\|esh-groups\\|subdirs\\)\\.el$\\)"
  28. "Regexp matching file names not to scan for `custom-make-dependencies'.")
  29. (require 'autoload)
  30. ;; Hack workaround for bug#14384.
  31. ;; Define defcustom-mh as an alias for defcustom, etc.
  32. ;; Only do this in batch mode to avoid messing up a normal Emacs session.
  33. ;; Alternative would be to load mh-e when making cus-load.
  34. ;; (Would be better to split just the necessary parts of mh-e into a
  35. ;; separate file and only load that.)
  36. (when (and noninteractive)
  37. (mapc (lambda (e) (let ((sym (intern (format "%s-mh" e))))
  38. (or (fboundp sym)
  39. (defalias sym e))))
  40. '(defcustom defface defgroup)))
  41. (defun custom-make-dependencies ()
  42. "Batch function to extract custom dependencies from .el files.
  43. Usage: emacs -batch -l ./cus-dep.el -f custom-make-dependencies DIRS"
  44. (let ((enable-local-eval nil)
  45. (enable-local-variables :safe)
  46. subdir)
  47. (with-temp-buffer
  48. ;; Use up command-line-args-left else Emacs can try to open
  49. ;; the args as directories after we are done.
  50. (while (setq subdir (pop command-line-args-left))
  51. (message "Scanning %s for custom" subdir)
  52. (let ((files (directory-files subdir nil "\\`[^=.].*\\.el\\'"))
  53. (default-directory
  54. (file-name-as-directory (expand-file-name subdir)))
  55. (preloaded (concat "\\`\\(\\./+\\)?"
  56. (regexp-opt preloaded-file-list t)
  57. "\\.el\\'")))
  58. (dolist (file files)
  59. (unless (or (string-match custom-dependencies-no-scan-regexp file)
  60. (string-match preloaded (format "%s/%s" subdir file))
  61. (not (file-exists-p file)))
  62. (erase-buffer)
  63. (kill-all-local-variables)
  64. (insert-file-contents file)
  65. (hack-local-variables)
  66. (goto-char (point-min))
  67. (string-match "\\`\\(.*\\)\\.el\\'" file)
  68. (let ((name (or generated-autoload-load-name ; see bug#5277
  69. (file-name-nondirectory (match-string 1 file))))
  70. (load-file-name file))
  71. (if (save-excursion
  72. (re-search-forward
  73. (concat "(\\(cc-\\)?provide[ \t\n]+\\('\\|(quote[ \t\n]\\)[ \t\n]*"
  74. (regexp-quote name) "[ \t\n)]")
  75. nil t))
  76. (setq name (intern name)))
  77. (condition-case nil
  78. (while (re-search-forward
  79. "^(def\\(custom\\|face\\|group\\)" nil t)
  80. (beginning-of-line)
  81. (let ((type (match-string 1))
  82. (expr (read (current-buffer))))
  83. (condition-case nil
  84. (let ((custom-dont-initialize t))
  85. ;; Eval to get the 'custom-group, -tag,
  86. ;; -version, group-documentation etc properties.
  87. (put (nth 1 expr) 'custom-where name)
  88. (eval expr))
  89. ;; Eval failed for some reason. Eg maybe the
  90. ;; defcustom uses something defined earlier
  91. ;; in the file (we haven't loaded the file).
  92. ;; In most cases, we can still get the :group.
  93. (error
  94. (ignore-errors
  95. (let ((group (cadr (memq :group expr))))
  96. (and group
  97. (eq (car group) 'quote)
  98. (custom-add-to-group
  99. (cadr group)
  100. (nth 1 expr)
  101. (intern (format "custom-%s"
  102. (if (equal type "custom")
  103. "variable"
  104. type)))))))))))
  105. (error nil)))))))))
  106. (message "Generating %s..." generated-custom-dependencies-file)
  107. (set-buffer (find-file-noselect generated-custom-dependencies-file))
  108. (setq buffer-undo-list t)
  109. (erase-buffer)
  110. (insert (autoload-rubric generated-custom-dependencies-file
  111. "custom dependencies" t))
  112. (search-backward " ")
  113. (let (alist)
  114. (mapatoms (lambda (symbol)
  115. (let ((members (get symbol 'custom-group))
  116. where found)
  117. (when members
  118. (dolist (member (mapcar 'car members))
  119. (setq where (get member 'custom-where))
  120. (unless (or (null where)
  121. (member where found))
  122. (push where found)))
  123. (when found
  124. (push (cons (symbol-name symbol)
  125. (with-output-to-string
  126. (prin1 (sort found 'string<)))) alist))))))
  127. (dolist (e (sort alist (lambda (e1 e2) (string< (car e1) (car e2)))))
  128. (insert "(put '" (car e) " 'custom-loads '" (cdr e) ")\n")))
  129. (insert "\
  130. ;; The remainder of this file is for handling :version.
  131. ;; We provide a minimum of information so that `customize-changed-options'
  132. ;; can do its job.
  133. ;; For groups we set `custom-version', `group-documentation' and
  134. ;; `custom-tag' (which are shown in the customize buffer), so we
  135. ;; don't have to load the file containing the group.
  136. ;; This macro is used so we don't modify the information about
  137. ;; variables and groups if it's already set. (We don't know when
  138. ;; " (file-name-nondirectory generated-custom-dependencies-file)
  139. " is going to be loaded and at that time some of the
  140. ;; files might be loaded and some others might not).
  141. \(defmacro custom-put-if-not (symbol propname value)
  142. `(unless (get ,symbol ,propname)
  143. (put ,symbol ,propname ,value)))
  144. ")
  145. (let ((version-alist nil)
  146. groups)
  147. (mapatoms (lambda (symbol)
  148. (let ((version (get symbol 'custom-version))
  149. where)
  150. (when version
  151. (setq where (get symbol 'custom-where))
  152. (when where
  153. (if (or (custom-variable-p symbol)
  154. (custom-facep symbol))
  155. ;; This means it's a variable or a face.
  156. (progn
  157. (if (assoc version version-alist)
  158. (unless
  159. (member where
  160. (cdr (assoc version version-alist)))
  161. (push where (cdr (assoc version version-alist))))
  162. (push (list version where) version-alist)))
  163. ;; This is a group
  164. (push (list (symbol-name symbol)
  165. (with-output-to-string (prin1 version))
  166. (with-output-to-string
  167. (prin1 (get symbol 'group-documentation)))
  168. (if (get symbol 'custom-tag)
  169. (with-output-to-string
  170. (prin1 (get symbol 'custom-tag)))))
  171. groups)))))))
  172. (dolist (e (sort groups (lambda (e1 e2) (string< (car e1) (car e2)))))
  173. (insert "(custom-put-if-not '" (car e) " 'custom-version '"
  174. (nth 1 e) ")\n")
  175. (insert "(custom-put-if-not '" (car e) " 'group-documentation "
  176. (nth 2 e) ")\n")
  177. (if (nth 3 e)
  178. (insert "(custom-put-if-not '" (car e) " 'custom-tag "
  179. (nth 3 e) ")\n")))
  180. (insert "\n(defvar custom-versions-load-alist "
  181. (if version-alist "'" ""))
  182. (prin1 (sort version-alist (lambda (e1 e2) (version< (car e1) (car e2))))
  183. (current-buffer))
  184. (insert "\n \"For internal use by custom.
  185. This is an alist whose members have as car a version string, and as
  186. elements the files that have variables or faces that contain that
  187. version. These files should be loaded before showing the customization
  188. buffer that `customize-changed-options' generates.\")\n\n"))
  189. (save-buffer)
  190. (message "Generating %s...done" generated-custom-dependencies-file))
  191. (provide 'cus-dep)
  192. ;;; cus-dep.el ends here