Update copyright year to 2014 by running admin/update-copyright.
[emacs.git] / lisp / cedet / ede / generic.el
blobd6f0385cf97a3fdb18299c3cc37e9930cfb0c880
1 ;;; ede/generic.el --- Base Support for generic build systems
3 ;; Copyright (C) 2010-2014 Free Software Foundation, Inc.
5 ;; Author: Eric M. Ludlam <eric@siege-engine.com>
7 ;; 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.
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
22 ;;; Commentary:
24 ;; There are a lot of build systems out there, and EDE can't support
25 ;; them all fully. The ede/generic.el system is the base for
26 ;; supporting alternate build systems in a simple way, automatically.
28 ;; The structure is for the ede-generic baseclass, which is augmented
29 ;; by simple sub-classes that can be created by users on an as needed
30 ;; basis. The generic system will have targets for many language
31 ;; types, and create the targets on an as needed basis. All
32 ;; sub-project types will recycle the same generic target types.
34 ;; The generic target types will only be implemented for languages
35 ;; where having EDE support actually matters, with a single MISC to
36 ;; represent anything else.
38 ;; TOO MANY PROJECTS DETECTED:
40 ;; If enabling ede-generic support starts identifying too many
41 ;; projects, drop a file called `.ede-ignore' into any directory where
42 ;; you do not want a project to be.
44 ;; Customization:
46 ;; Since these projects are all so incredibly generic, a user will
47 ;; need to configure some aspects of the project by hand. In order to
48 ;; enable this without configuring the project objects directly (which
49 ;; are auto-generated) a special ede-generic-config object is defined to
50 ;; hold the basics. Generic projects will identify and use these
51 ;; config files.
53 ;; Adding support for new projects:
55 ;; To add support to EDE Generic for new project types is very quick.
56 ;; See the end of this file for examples such as CMake and SCons.
58 ;; Support consists of one class for your project, specifying the file
59 ;; name used by the project system you want to support. It also
60 ;; should implement th method `ede-generic-setup-configuration' to
61 ;; prepopulate the configurable portion of the generic project with
62 ;; build details.
64 ;; Lastly, call `ede-generic-new-autoloader' to setup your project so
65 ;; EDE can use it.
67 ;; Adding support for new types of source code:
69 ;; Sources of different types are supported with a simple class which
70 ;; subclasses `ede-generic-target'. The slots `shortname' and
71 ;; `extension' should be given new initial values.
73 ;; Optionally, any target method used by EDE can then be overridden.
74 ;; The ede-generic-target-c-cpp has some example methods setting up
75 ;; the pre-processor map and system include path.
77 ;; NOTE: It is not necessary to modify ede-generic.el to add any of
78 ;; the above described support features.
80 (require 'eieio-opt)
81 (require 'ede)
82 (require 'ede/shell)
83 (require 'semantic/db)
85 ;;; Code:
87 ;; Start with the configuration system
88 (defclass ede-generic-config (eieio-persistent)
89 ((extension :initform ".ede")
90 (file-header-line :initform ";; EDE Generic Project Configuration")
91 (project :initform nil
92 :documentation
93 "The project this config is bound to.")
94 ;; Generic customizations
95 (build-command :initarg :build-command
96 :initform "make -k"
97 :type string
98 :custom string
99 :group (default build)
100 :documentation
101 "Command used for building this project.")
102 (debug-command :initarg :debug-command
103 :initform "gdb "
104 :type string
105 :custom string
106 :group (default build)
107 :documentation
108 "Command used for debugging this project.")
109 (run-command :initarg :run-command
110 :initform nil
111 :type (or null string)
112 :custom string
113 :group (default build)
114 :documentation
115 "Command used to run something related to this project.")
116 ;; C target customizations
117 (c-include-path :initarg :c-include-path
118 :initform nil
119 :type list
120 :custom (repeat (string :tag "Path"))
121 :group c
122 :documentation
123 "The include path used by C/C++ projects.")
124 (c-preprocessor-table :initarg :c-preprocessor-table
125 :initform nil
126 :type list
127 :custom (repeat (cons (string :tag "Macro")
128 (string :tag "Value")))
129 :group c
130 :documentation
131 "Preprocessor Symbols for this project.")
132 (c-preprocessor-files :initarg :c-preprocessor-files
133 :initform nil
134 :type list
135 :custom (repeat (string :tag "Include File")))
137 "User Configuration object for a generic project.")
139 (defun ede-generic-load (dir &optional rootproj)
140 "Return a Generic Project object if there is a match.
141 Return nil if there isn't one.
142 Argument DIR is the directory it is created for.
143 ROOTPROJ is nil, since there is only one project."
144 ;; Doesn't already exist, so let's make one.
145 (let* ((alobj ede-constructing)
146 (this nil))
147 (when (not alobj) (error "Cannot load generic project without the autoload instance"))
149 (setq this
150 (funcall (oref alobj class-sym)
151 (symbol-name (oref alobj class-sym))
152 :name (file-name-nondirectory
153 (directory-file-name dir))
154 :version "1.0"
155 :directory (file-name-as-directory dir)
156 :file (expand-file-name (oref alobj :proj-file)) ))
157 (ede-add-project-to-global-list this)
160 ;;; Base Classes for the system
161 (defclass ede-generic-target (ede-target)
162 ((shortname :initform ""
163 :type string
164 :allocation :class
165 :documentation
166 "Something prepended to the target name.")
167 (extension :initform ""
168 :type string
169 :allocation :class
170 :documentation
171 "Regular expression representing the extension used for this target.
172 subclasses of this base target will override the default value.")
174 "Baseclass for all targets belonging to the generic ede system."
175 :abstract t)
177 (defclass ede-generic-project (ede-project)
178 ((buildfile :initform ""
179 :type string
180 :allocation :class
181 :documentation "The file name that identifies a project of this type.
182 The class allocated value is replace by different sub classes.")
183 (config :initform nil
184 :type (or null ede-generic-config)
185 :documentation
186 "The configuration object for this project.")
188 "The baseclass for all generic EDE project types."
189 :abstract t)
191 (defmethod initialize-instance ((this ede-generic-project)
192 &rest fields)
193 "Make sure the targets slot is bound."
194 (call-next-method)
195 (unless (slot-boundp this 'targets)
196 (oset this :targets nil))
199 (defmethod ede-generic-get-configuration ((proj ede-generic-project))
200 "Return the configuration for the project PROJ."
201 (let ((config (oref proj config)))
202 (when (not config)
203 (let ((fname (expand-file-name "EDEConfig.el"
204 (oref proj :directory))))
205 (if (file-exists-p fname)
206 ;; Load in the configuration
207 (setq config (eieio-persistent-read fname 'ede-generic-config))
208 ;; Create a new one.
209 (setq config (ede-generic-config
210 "Configuration"
211 :file fname))
212 ;; Set initial values based on project.
213 (ede-generic-setup-configuration proj config))
214 ;; Link things together.
215 (oset proj config config)
216 (oset config project proj)))
217 config))
219 (defmethod ede-generic-setup-configuration ((proj ede-generic-project) config)
220 "Default configuration setup method."
221 nil)
223 (defmethod ede-commit-project ((proj ede-generic-project))
224 "Commit any change to PROJ to its file."
225 (let ((config (ede-generic-get-configuration proj)))
226 (ede-commit config)))
228 ;;; A list of different targets
229 (defclass ede-generic-target-c-cpp (ede-generic-target)
230 ((shortname :initform "C/C++")
231 (extension :initform "\\([ch]\\(pp\\|xx\\|\\+\\+\\)?\\|cc\\|hh\\|CC?\\)"))
232 "EDE Generic Project target for C and C++ code.
233 All directories need at least one target.")
235 (defclass ede-generic-target-el (ede-generic-target)
236 ((shortname :initform "ELisp")
237 (extension :initform "el"))
238 "EDE Generic Project target for Emacs Lisp code.
239 All directories need at least one target.")
241 (defclass ede-generic-target-fortran (ede-generic-target)
242 ((shortname :initform "Fortran")
243 (extension :initform "[fF]9[05]\\|[fF]\\|for"))
244 "EDE Generic Project target for Fortran code.
245 All directories need at least one target.")
247 (defclass ede-generic-target-texi (ede-generic-target)
248 ((shortname :initform "Texinfo")
249 (extension :initform "texi"))
250 "EDE Generic Project target for texinfo code.
251 All directories need at least one target.")
253 ;; MISC must always be last since it will always match the file.
254 (defclass ede-generic-target-misc (ede-generic-target)
255 ((shortname :initform "Misc")
256 (extension :initform ""))
257 "EDE Generic Project target for Misc files.
258 All directories need at least one target.")
260 ;;; Automatic target acquisition.
261 (defun ede-generic-find-matching-target (class dir targets)
262 "Find a target that is a CLASS and is in DIR in the list of TARGETS."
263 (let ((match nil))
264 (dolist (T targets)
265 (when (and (object-of-class-p T class)
266 (string= (oref T :path) dir))
267 (setq match T)
269 match))
271 (defmethod ede-find-target ((proj ede-generic-project) buffer)
272 "Find an EDE target in PROJ for BUFFER.
273 If one doesn't exist, create a new one for this directory."
274 (let* ((ext (file-name-extension (buffer-file-name buffer)))
275 (classes (eieio-build-class-alist 'ede-generic-target t))
276 (cls nil)
277 (targets (oref proj targets))
278 (dir default-directory)
279 (ans nil)
281 ;; Pick a matching class type.
282 (when ext
283 (dolist (C classes)
284 (let* ((classsym (intern (car C)))
285 (extreg (oref classsym extension)))
286 (when (and (not (string= extreg ""))
287 (string-match (concat "^" extreg "$") ext))
288 (setq cls classsym)))))
289 (when (not cls) (setq cls 'ede-generic-target-misc))
290 ;; find a pre-existing matching target
291 (setq ans (ede-generic-find-matching-target cls dir targets))
292 ;; Create a new instance if there wasn't one
293 (when (not ans)
294 (setq ans (make-instance
296 :name (oref cls shortname)
297 :path dir
298 :source nil))
299 (object-add-to-list proj :targets ans)
301 ans))
303 ;;; C/C++ support
304 (defmethod ede-preprocessor-map ((this ede-generic-target-c-cpp))
305 "Get the pre-processor map for some generic C code."
306 (let* ((proj (ede-target-parent this))
307 (root (ede-project-root proj))
308 (config (ede-generic-get-configuration proj))
309 filemap
311 ;; Preprocessor files
312 (dolist (G (oref config :c-preprocessor-files))
313 (let ((table (semanticdb-file-table-object
314 (ede-expand-filename root G))))
315 (when table
316 (when (semanticdb-needs-refresh-p table)
317 (semanticdb-refresh-table table))
318 (setq filemap (append filemap (oref table lexical-table)))
320 ;; The core table
321 (setq filemap (append filemap (oref config :c-preprocessor-table)))
323 filemap
326 (defmethod ede-system-include-path ((this ede-generic-target-c-cpp))
327 "Get the system include path used by project THIS."
328 (let* ((proj (ede-target-parent this))
329 (config (ede-generic-get-configuration proj)))
330 (oref config c-include-path)))
332 ;;; Commands
334 (defmethod project-compile-project ((proj ede-generic-project) &optional command)
335 "Compile the entire current project PROJ.
336 Argument COMMAND is the command to use when compiling."
337 (let* ((config (ede-generic-get-configuration proj))
338 (comp (oref config :build-command)))
339 (compile comp)))
341 (defmethod project-compile-target ((obj ede-generic-target) &optional command)
342 "Compile the current target OBJ.
343 Argument COMMAND is the command to use for compiling the target."
344 (project-compile-project (ede-current-project) command))
346 (defmethod project-debug-target ((target ede-generic-target))
347 "Run the current project derived from TARGET in a debugger."
348 (let* ((proj (ede-target-parent target))
349 (config (ede-generic-get-configuration proj))
350 (debug (oref config :debug-command))
351 (cmd (read-from-minibuffer
352 "Debug Command: "
353 debug))
354 (cmdsplit (split-string cmd " " t))
355 ;; @TODO - this depends on the user always typing in something good
356 ;; like "gdb" or "dbx" which also exists as a useful Emacs command.
357 ;; Is there a better way?
358 (cmdsym (intern-soft (car cmdsplit))))
359 (call-interactively cmdsym t)))
361 (defmethod project-run-target ((target ede-generic-target))
362 "Run the current project derived from TARGET."
363 (let* ((proj (ede-target-parent target))
364 (config (ede-generic-get-configuration proj))
365 (run (concat "./" (oref config :run-command)))
366 (cmd (read-from-minibuffer "Run (like this): " run)))
367 (ede-shell-run-something target cmd)))
369 ;;; Customization
371 (defmethod ede-customize ((proj ede-generic-project))
372 "Customize the EDE project PROJ."
373 (let ((config (ede-generic-get-configuration proj)))
374 (eieio-customize-object config)))
376 (defmethod ede-customize ((target ede-generic-target))
377 "Customize the EDE TARGET."
378 ;; Nothing unique for the targets, use the project.
379 (ede-customize-project))
381 (defmethod eieio-done-customizing ((config ede-generic-config))
382 "Called when EIEIO is done customizing the configuration object.
383 We need to go back through the old buffers, and update them with
384 the new configuration."
385 (ede-commit config)
386 ;; Loop over all the open buffers, and re-apply.
387 (ede-map-targets
388 (oref config project)
389 (lambda (target)
390 (ede-map-target-buffers
391 target
392 (lambda (b)
393 (with-current-buffer b
394 (ede-apply-target-options)))))))
396 (defmethod ede-commit ((config ede-generic-config))
397 "Commit all changes to the configuration to disk."
398 (eieio-persistent-save config))
400 ;;; Creating Derived Projects:
402 ;; Derived projects need an autoloader so that EDE can find the
403 ;; different projects on disk.
404 (defun ede-generic-new-autoloader (internal-name external-name
405 projectfile class)
406 "Add a new EDE Autoload instance for identifying a generic project.
407 INTERNAL-NAME is a long name that identifies this project type.
408 EXTERNAL-NAME is a shorter human readable name to describe the project.
409 PROJECTFILE is a file name that identifies a project of this type to EDE, such as
410 a Makefile, or SConstruct file.
411 CLASS is the EIEIO class that is used to track this project. It should subclass
412 the class `ede-generic-project' project."
413 (ede-add-project-autoload
414 (ede-project-autoload internal-name
415 :name external-name
416 :file 'ede/generic
417 :proj-file projectfile
418 :load-type 'ede-generic-load
419 :class-sym class
420 :new-p nil
421 :safe-p nil) ; @todo - could be
422 ; safe if we do something
423 ; about the loading of the
424 ; generic config file.
425 ;; Generics must go at the end, since more specific types
426 ;; can create Makefiles also.
427 'generic))
429 ;;;###autoload
430 (defun ede-enable-generic-projects ()
431 "Enable generic project loaders."
432 (interactive)
433 (ede-generic-new-autoloader "generic-makefile" "Make"
434 "Makefile" 'ede-generic-makefile-project)
435 (ede-generic-new-autoloader "generic-scons" "SCons"
436 "SConstruct" 'ede-generic-scons-project)
437 (ede-generic-new-autoloader "generic-cmake" "CMake"
438 "CMakeLists" 'ede-generic-cmake-project)
442 ;;; SPECIFIC TYPES OF GENERIC BUILDS
445 ;;; MAKEFILE
447 (defclass ede-generic-makefile-project (ede-generic-project)
448 ((buildfile :initform "Makefile")
450 "Generic Project for makefiles.")
452 (defmethod ede-generic-setup-configuration ((proj ede-generic-makefile-project) config)
453 "Setup a configuration for Make."
454 (oset config build-command "make -k")
455 (oset config debug-command "gdb ")
459 ;;; SCONS
460 (defclass ede-generic-scons-project (ede-generic-project)
461 ((buildfile :initform "SConstruct")
463 "Generic Project for scons.")
465 (defmethod ede-generic-setup-configuration ((proj ede-generic-scons-project) config)
466 "Setup a configuration for SCONS."
467 (oset config build-command "scons")
468 (oset config debug-command "gdb ")
472 ;;; CMAKE
473 (defclass ede-generic-cmake-project (ede-generic-project)
474 ((buildfile :initform "CMakeLists")
476 "Generic Project for cmake.")
478 (defmethod ede-generic-setup-configuration ((proj ede-generic-cmake-project) config)
479 "Setup a configuration for CMake."
480 (oset config build-command "cmake")
481 (oset config debug-command "gdb ")
484 (provide 'ede/generic)
486 ;; Local variables:
487 ;; generated-autoload-file: "loaddefs.el"
488 ;; generated-autoload-load-name: "ede/generic"
489 ;; End:
491 ;;; ede/generic.el ends here