generic.el 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. ;;; ede/generic.el --- Base Support for generic build systems
  2. ;; Copyright (C) 2010-2012 Free Software Foundation, Inc.
  3. ;; Author: Eric M. Ludlam <eric@siege-engine.com>
  4. ;; This file is part of GNU Emacs.
  5. ;; GNU Emacs is free software: you can redistribute it and/or modify
  6. ;; it under the terms of the GNU General Public License as published by
  7. ;; the Free Software Foundation, either version 3 of the License, or
  8. ;; (at your option) any later version.
  9. ;; GNU Emacs 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
  12. ;; GNU General Public License for more details.
  13. ;; You should have received a copy of the GNU General Public License
  14. ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
  15. ;;; Commentary:
  16. ;;
  17. ;; There are a lot of build systems out there, and EDE can't support
  18. ;; them all fully. The ede/generic.el system is the base for
  19. ;; supporting alternate build systems in a simple way, automatically.
  20. ;;
  21. ;; The structure is for the ede-generic baseclass, which is augmented
  22. ;; by simple sub-classes that can be created by users on an as needed
  23. ;; basis. The generic system will have targets for many language
  24. ;; types, and create the targets on an as needed basis. All
  25. ;; sub-project types will recycle the same generic target types.
  26. ;;
  27. ;; The generic target types will only be implemented for languages
  28. ;; where having EDE support actually matters, with a single MISC to
  29. ;; represent anything else.
  30. ;;
  31. ;; TOO MANY PROJECTS DETECTED:
  32. ;;
  33. ;; If enabling ede-generic support starts identifying too many
  34. ;; projects, drop a file called `.ede-ignore' into any directory where
  35. ;; you do not want a project to be.
  36. ;;
  37. ;; Customization:
  38. ;;
  39. ;; Since these projects are all so incredibly generic, a user will
  40. ;; need to configure some aspects of the project by hand. In order to
  41. ;; enable this without configuring the project objects directly (which
  42. ;; are auto-generated) a special ede-generic-config object is defined to
  43. ;; hold the basics. Generic projects will identify and use these
  44. ;; config files.
  45. ;;
  46. ;; Adding support for new projects:
  47. ;;
  48. ;; To add support to EDE Generic for new project types is very quick.
  49. ;; See the end of this file for examples such as CMake and SCons.
  50. ;;
  51. ;; Support consists of one class for your project, specifying the file
  52. ;; name used by the project system you want to support. It also
  53. ;; should implement th method `ede-generic-setup-configuration' to
  54. ;; prepopulate the configurable portion of the generic project with
  55. ;; build details.
  56. ;;
  57. ;; Lastly, call `ede-generic-new-autoloader' to setup your project so
  58. ;; EDE can use it.
  59. ;;
  60. ;; Adding support for new types of source code:
  61. ;;
  62. ;; Sources of different types are supported with a simple class which
  63. ;; subclasses `ede-generic-target'. The slots `shortname' and
  64. ;; `extension' should be given new initial values.
  65. ;;
  66. ;; Optionally, any target method used by EDE can then be overridden.
  67. ;; The ede-generic-target-c-cpp has some example methods setting up
  68. ;; the pre-processor map and system include path.
  69. ;;
  70. ;; NOTE: It is not necessary to modify ede-generic.el to add any of
  71. ;; the above described support features.
  72. (require 'eieio-opt)
  73. (require 'ede)
  74. (require 'semantic/db)
  75. ;;; Code:
  76. ;;
  77. ;; Start with the configuration system
  78. (defclass ede-generic-config (eieio-persistent)
  79. ((extension :initform ".ede")
  80. (file-header-line :initform ";; EDE Generic Project Configuration")
  81. (project :initform nil
  82. :documentation
  83. "The project this config is bound to.")
  84. ;; Generic customizations
  85. (build-command :initarg :build-command
  86. :initform "make -k"
  87. :type string
  88. :custom string
  89. :group (default build)
  90. :documentation
  91. "Command used for building this project.")
  92. (debug-command :initarg :debug-command
  93. :initform "gdb "
  94. :type string
  95. :custom string
  96. :group (default build)
  97. :documentation
  98. "Command used for debugging this project.")
  99. ;; C target customizations
  100. (c-include-path :initarg :c-include-path
  101. :initform nil
  102. :type list
  103. :custom (repeat (string :tag "Path"))
  104. :group c
  105. :documentation
  106. "The include path used by C/C++ projects.")
  107. (c-preprocessor-table :initarg :c-preprocessor-table
  108. :initform nil
  109. :type list
  110. :custom (repeat (cons (string :tag "Macro")
  111. (string :tag "Value")))
  112. :group c
  113. :documentation
  114. "Preprocessor Symbols for this project.")
  115. (c-preprocessor-files :initarg :c-preprocessor-files
  116. :initform nil
  117. :type list
  118. :custom (repeat (string :tag "Include File")))
  119. )
  120. "User Configuration object for a generic project.")
  121. (defun ede-generic-load (dir &optional rootproj)
  122. "Return a Generic Project object if there is a match.
  123. Return nil if there isn't one.
  124. Argument DIR is the directory it is created for.
  125. ROOTPROJ is nil, since there is only one project."
  126. ;; Doesn't already exist, so let's make one.
  127. (let* ((alobj ede-constructing)
  128. (this nil))
  129. (when (not alobj) (error "Cannot load generic project without the autoload instance"))
  130. (setq this
  131. (funcall (oref alobj class-sym)
  132. (symbol-name (oref alobj class-sym))
  133. :name (file-name-nondirectory
  134. (directory-file-name dir))
  135. :version "1.0"
  136. :directory (file-name-as-directory dir)
  137. :file (expand-file-name (oref alobj :proj-file)) ))
  138. (ede-add-project-to-global-list this)
  139. ))
  140. ;;; Base Classes for the system
  141. (defclass ede-generic-target (ede-target)
  142. ((shortname :initform ""
  143. :type string
  144. :allocation :class
  145. :documentation
  146. "Something prepended to the target name.")
  147. (extension :initform ""
  148. :type string
  149. :allocation :class
  150. :documentation
  151. "Regular expression representing the extension used for this target.
  152. subclasses of this base target will override the default value.")
  153. )
  154. "Baseclass for all targets belonging to the generic ede system."
  155. :abstract t)
  156. (defclass ede-generic-project (ede-project)
  157. ((buildfile :initform ""
  158. :type string
  159. :allocation :class
  160. :documentation "The file name that identifies a project of this type.
  161. The class allocated value is replace by different sub classes.")
  162. (config :initform nil
  163. :type (or null ede-generic-config)
  164. :documentation
  165. "The configuration object for this project.")
  166. )
  167. "The baseclass for all generic EDE project types."
  168. :abstract t)
  169. (defmethod initialize-instance ((this ede-generic-project)
  170. &rest fields)
  171. "Make sure the targets slot is bound."
  172. (call-next-method)
  173. (unless (slot-boundp this 'targets)
  174. (oset this :targets nil))
  175. )
  176. (defmethod ede-generic-get-configuration ((proj ede-generic-project))
  177. "Return the configuration for the project PROJ."
  178. (let ((config (oref proj config)))
  179. (when (not config)
  180. (let ((fname (expand-file-name "EDEConfig.el"
  181. (oref proj :directory))))
  182. (if (file-exists-p fname)
  183. ;; Load in the configuration
  184. (setq config (eieio-persistent-read fname))
  185. ;; Create a new one.
  186. (setq config (ede-generic-config
  187. "Configuration"
  188. :file fname))
  189. ;; Set initial values based on project.
  190. (ede-generic-setup-configuration proj config))
  191. ;; Link things together.
  192. (oset proj config config)
  193. (oset config project proj)))
  194. config))
  195. (defmethod ede-generic-setup-configuration ((proj ede-generic-project) config)
  196. "Default configuration setup method."
  197. nil)
  198. (defmethod ede-commit-project ((proj ede-generic-project))
  199. "Commit any change to PROJ to its file."
  200. (let ((config (ede-generic-get-configuration proj)))
  201. (ede-commit config)))
  202. ;;; A list of different targets
  203. (defclass ede-generic-target-c-cpp (ede-generic-target)
  204. ((shortname :initform "C/C++")
  205. (extension :initform "\\([ch]\\(pp\\|xx\\|\\+\\+\\)?\\|cc\\|hh\\|CC?\\)"))
  206. "EDE Generic Project target for C and C++ code.
  207. All directories need at least one target.")
  208. (defclass ede-generic-target-el (ede-generic-target)
  209. ((shortname :initform "ELisp")
  210. (extension :initform "el"))
  211. "EDE Generic Project target for Emacs Lisp code.
  212. All directories need at least one target.")
  213. (defclass ede-generic-target-fortran (ede-generic-target)
  214. ((shortname :initform "Fortran")
  215. (extension :initform "[fF]9[05]\\|[fF]\\|for"))
  216. "EDE Generic Project target for Fortran code.
  217. All directories need at least one target.")
  218. (defclass ede-generic-target-texi (ede-generic-target)
  219. ((shortname :initform "Texinfo")
  220. (extension :initform "texi"))
  221. "EDE Generic Project target for texinfo code.
  222. All directories need at least one target.")
  223. ;; MISC must always be last since it will always match the file.
  224. (defclass ede-generic-target-misc (ede-generic-target)
  225. ((shortname :initform "Misc")
  226. (extension :initform ""))
  227. "EDE Generic Project target for Misc files.
  228. All directories need at least one target.")
  229. ;;; Automatic target acquisition.
  230. (defun ede-generic-find-matching-target (class dir targets)
  231. "Find a target that is a CLASS and is in DIR in the list of TARGETS."
  232. (let ((match nil))
  233. (dolist (T targets)
  234. (when (and (object-of-class-p T class)
  235. (string= (oref T :path) dir))
  236. (setq match T)
  237. ))
  238. match))
  239. (defmethod ede-find-target ((proj ede-generic-project) buffer)
  240. "Find an EDE target in PROJ for BUFFER.
  241. If one doesn't exist, create a new one for this directory."
  242. (let* ((ext (file-name-extension (buffer-file-name buffer)))
  243. (classes (eieio-build-class-alist 'ede-generic-target t))
  244. (cls nil)
  245. (targets (oref proj targets))
  246. (dir default-directory)
  247. (ans nil)
  248. )
  249. ;; Pick a matching class type.
  250. (when ext
  251. (dolist (C classes)
  252. (let* ((classsym (intern (car C)))
  253. (extreg (oref classsym extension)))
  254. (when (and (not (string= extreg ""))
  255. (string-match (concat "^" extreg "$") ext))
  256. (setq cls classsym)))))
  257. (when (not cls) (setq cls 'ede-generic-target-misc))
  258. ;; find a pre-existing matching target
  259. (setq ans (ede-generic-find-matching-target cls dir targets))
  260. ;; Create a new instance if there wasn't one
  261. (when (not ans)
  262. (setq ans (make-instance
  263. cls
  264. :name (oref cls shortname)
  265. :path dir
  266. :source nil))
  267. (object-add-to-list proj :targets ans)
  268. )
  269. ans))
  270. ;;; C/C++ support
  271. (defmethod ede-preprocessor-map ((this ede-generic-target-c-cpp))
  272. "Get the pre-processor map for some generic C code."
  273. (let* ((proj (ede-target-parent this))
  274. (root (ede-project-root proj))
  275. (config (ede-generic-get-configuration proj))
  276. filemap
  277. )
  278. ;; Preprocessor files
  279. (dolist (G (oref config :c-preprocessor-files))
  280. (let ((table (semanticdb-file-table-object
  281. (ede-expand-filename root G))))
  282. (when table
  283. (when (semanticdb-needs-refresh-p table)
  284. (semanticdb-refresh-table table))
  285. (setq filemap (append filemap (oref table lexical-table)))
  286. )))
  287. ;; The core table
  288. (setq filemap (append filemap (oref config :c-preprocessor-table)))
  289. filemap
  290. ))
  291. (defmethod ede-system-include-path ((this ede-generic-target-c-cpp))
  292. "Get the system include path used by project THIS."
  293. (let* ((proj (ede-target-parent this))
  294. (config (ede-generic-get-configuration proj)))
  295. (oref config c-include-path)))
  296. ;;; Customization
  297. ;;
  298. (defmethod ede-customize ((proj ede-generic-project))
  299. "Customize the EDE project PROJ."
  300. (let ((config (ede-generic-get-configuration proj)))
  301. (eieio-customize-object config)))
  302. (defmethod ede-customize ((target ede-generic-target))
  303. "Customize the EDE TARGET."
  304. ;; Nothing unique for the targets, use the project.
  305. (ede-customize-project))
  306. (defmethod eieio-done-customizing ((config ede-generic-config))
  307. "Called when EIEIO is done customizing the configuration object.
  308. We need to go back through the old buffers, and update them with
  309. the new configuration."
  310. (ede-commit config)
  311. ;; Loop over all the open buffers, and re-apply.
  312. (ede-map-targets
  313. (oref config project)
  314. (lambda (target)
  315. (ede-map-target-buffers
  316. target
  317. (lambda (b)
  318. (with-current-buffer b
  319. (ede-apply-target-options)))))))
  320. (defmethod ede-commit ((config ede-generic-config))
  321. "Commit all changes to the configuration to disk."
  322. (eieio-persistent-save config))
  323. ;;; Creating Derived Projects:
  324. ;;
  325. ;; Derived projects need an autoloader so that EDE can find the
  326. ;; different projects on disk.
  327. (defun ede-generic-new-autoloader (internal-name external-name
  328. projectfile class)
  329. "Add a new EDE Autoload instance for identifying a generic project.
  330. INTERNAL-NAME is a long name that identifies this project type.
  331. EXTERNAL-NAME is a shorter human readable name to describe the project.
  332. PROJECTFILE is a file name that identifies a project of this type to EDE, such as
  333. a Makefile, or SConstruct file.
  334. CLASS is the EIEIO class that is used to track this project. It should subclass
  335. the class `ede-generic-project' project."
  336. (add-to-list 'ede-project-class-files
  337. (ede-project-autoload internal-name
  338. :name external-name
  339. :file 'ede/generic
  340. :proj-file projectfile
  341. :load-type 'ede-generic-load
  342. :class-sym class
  343. :new-p nil)
  344. ;; Generics must go at the end, since more specific types
  345. ;; can create Makefiles also.
  346. t))
  347. ;;;###autoload
  348. (defun ede-enable-generic-projects ()
  349. "Enable generic project loaders."
  350. (interactive)
  351. (ede-generic-new-autoloader "edeproject-makefile" "Make"
  352. "Makefile" 'ede-generic-makefile-project)
  353. (ede-generic-new-autoloader "edeproject-scons" "SCons"
  354. "SConstruct" 'ede-generic-scons-project)
  355. (ede-generic-new-autoloader "edeproject-cmake" "CMake"
  356. "CMakeLists" 'ede-generic-cmake-project)
  357. )
  358. ;;; SPECIFIC TYPES OF GENERIC BUILDS
  359. ;;
  360. ;;; MAKEFILE
  361. (defclass ede-generic-makefile-project (ede-generic-project)
  362. ((buildfile :initform "Makefile")
  363. )
  364. "Generic Project for makefiles.")
  365. (defmethod ede-generic-setup-configuration ((proj ede-generic-makefile-project) config)
  366. "Setup a configuration for Make."
  367. (oset config build-command "make -k")
  368. (oset config debug-command "gdb ")
  369. )
  370. ;;; SCONS
  371. (defclass ede-generic-scons-project (ede-generic-project)
  372. ((buildfile :initform "SConstruct")
  373. )
  374. "Generic Project for scons.")
  375. (defmethod ede-generic-setup-configuration ((proj ede-generic-scons-project) config)
  376. "Setup a configuration for SCONS."
  377. (oset config build-command "scons")
  378. (oset config debug-command "gdb ")
  379. )
  380. ;;; CMAKE
  381. (defclass ede-generic-cmake-project (ede-generic-project)
  382. ((buildfile :initform "CMakeLists")
  383. )
  384. "Generic Project for cmake.")
  385. (defmethod ede-generic-setup-configuration ((proj ede-generic-cmake-project) config)
  386. "Setup a configuration for CMake."
  387. (oset config build-command "cmake")
  388. (oset config debug-command "gdb ")
  389. )
  390. (provide 'ede/generic)
  391. ;; Local variables:
  392. ;; generated-autoload-file: "loaddefs.el"
  393. ;; generated-autoload-load-name: "ede/generic"
  394. ;; End:
  395. ;;; ede/generic.el ends here