Merge with CEDET upstream.
[bpt/emacs.git] / lisp / cedet / ede / generic.el
CommitLineData
cb85c0d8
EL
1;;; ede/generic.el --- Base Support for generic build systems
2
ab422c4d 3;; Copyright (C) 2010-2013 Free Software Foundation, Inc.
cb85c0d8
EL
4
5;; Author: Eric M. Ludlam <eric@siege-engine.com>
6
7;; This file is part of GNU Emacs.
8
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
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.
18
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/>.
21
22;;; Commentary:
23;;
24;; There are a lot of build systems out there, and EDE can't support
07a79ce4 25;; them all fully. The ede/generic.el system is the base for
cb85c0d8
EL
26;; supporting alternate build systems in a simple way, automatically.
27;;
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.
33;;
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.
37;;
38;; TOO MANY PROJECTS DETECTED:
39;;
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.
43;;
44;; Customization:
45;;
53964682 46;; Since these projects are all so incredibly generic, a user will
cb85c0d8
EL
47;; need to configure some aspects of the project by hand. In order to
48;; enable this without configuring the project objects directly (which
da6062e6 49;; are auto-generated) a special ede-generic-config object is defined to
cb85c0d8
EL
50;; hold the basics. Generic projects will identify and use these
51;; config files.
52;;
53;; Adding support for new projects:
54;;
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.
57;;
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.
63;;
64;; Lastly, call `ede-generic-new-autoloader' to setup your project so
65;; EDE can use it.
66;;
67;; Adding support for new types of source code:
68;;
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.
72;;
0b381c7e 73;; Optionally, any target method used by EDE can then be overridden.
cb85c0d8
EL
74;; The ede-generic-target-c-cpp has some example methods setting up
75;; the pre-processor map and system include path.
76;;
77;; NOTE: It is not necessary to modify ede-generic.el to add any of
78;; the above described support features.
79
80(require 'eieio-opt)
81(require 'ede)
62a81506 82(require 'ede/shell)
cb85c0d8
EL
83(require 'semantic/db)
84
85;;; Code:
86;;
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.")
62a81506
CY
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.")
da6062e6 116 ;; C target customizations
cb85c0d8
EL
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")))
136 )
137 "User Configuration object for a generic project.")
138
139(defun ede-generic-load (dir &optional rootproj)
140 "Return a Generic Project object if there is a match.
141Return nil if there isn't one.
142Argument DIR is the directory it is created for.
143ROOTPROJ is nil, since there is only one project."
c7015153 144 ;; Doesn't already exist, so let's make one.
cb85c0d8
EL
145 (let* ((alobj ede-constructing)
146 (this nil))
147 (when (not alobj) (error "Cannot load generic project without the autoload instance"))
148
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)
158 ))
159
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.
172subclasses of this base target will override the default value.")
173 )
174 "Baseclass for all targets belonging to the generic ede system."
175 :abstract t)
176
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.
182The 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.")
187 )
188 "The baseclass for all generic EDE project types."
189 :abstract t)
190
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))
197 )
198
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
62a81506 207 (setq config (eieio-persistent-read fname 'ede-generic-config))
cb85c0d8
EL
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))
218
219(defmethod ede-generic-setup-configuration ((proj ede-generic-project) config)
220 "Default configuration setup method."
221 nil)
222
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)))
227
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.
233All directories need at least one target.")
234
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.
239All directories need at least one target.")
240
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.
245All directories need at least one target.")
246
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.
251All directories need at least one target.")
252
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.
258All directories need at least one target.")
259
91af3942 260;;; Automatic target acquisition.
cb85c0d8
EL
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)
268 ))
269 match))
270
271(defmethod ede-find-target ((proj ede-generic-project) buffer)
272 "Find an EDE target in PROJ for BUFFER.
273If 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)
280 )
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
295 cls
296 :name (oref cls shortname)
297 :path dir
298 :source nil))
299 (object-add-to-list proj :targets ans)
300 )
301 ans))
302
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
310 )
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)))
319 )))
320 ;; The core table
321 (setq filemap (append filemap (oref config :c-preprocessor-table)))
322
323 filemap
324 ))
325
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)))
331
62a81506
CY
332;;; Commands
333;;
334(defmethod project-compile-project ((proj ede-generic-project) &optional command)
335 "Compile the entire current project PROJ.
336Argument 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)))
340
341(defmethod project-compile-target ((obj ede-generic-target) &optional command)
342 "Compile the current target OBJ.
343Argument COMMAND is the command to use for compiling the target."
344 (project-compile-project (ede-current-project) command))
345
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)))
360
361(defmethod project-run-target ((target ede-generic-target))
362 "Run the current project derived from TARGET."
62a81506
CY
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)))
368
cb85c0d8
EL
369;;; Customization
370;;
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)))
375
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))
380
381(defmethod eieio-done-customizing ((config ede-generic-config))
382 "Called when EIEIO is done customizing the configuration object.
383We need to go back through the old buffers, and update them with
384the 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)))))))
395
396(defmethod ede-commit ((config ede-generic-config))
397 "Commit all changes to the configuration to disk."
398 (eieio-persistent-save config))
399
400;;; Creating Derived Projects:
401;;
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.
40ba43b4 407INTERNAL-NAME is a long name that identifies this project type.
cb85c0d8
EL
408EXTERNAL-NAME is a shorter human readable name to describe the project.
409PROJECTFILE is a file name that identifies a project of this type to EDE, such as
410a Makefile, or SConstruct file.
411CLASS is the EIEIO class that is used to track this project. It should subclass
412the class `ede-generic-project' project."
62a81506
CY
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))
cb85c0d8
EL
428
429;;;###autoload
430(defun ede-enable-generic-projects ()
431 "Enable generic project loaders."
432 (interactive)
62a81506 433 (ede-generic-new-autoloader "generic-makefile" "Make"
cb85c0d8 434 "Makefile" 'ede-generic-makefile-project)
62a81506 435 (ede-generic-new-autoloader "generic-scons" "SCons"
cb85c0d8 436 "SConstruct" 'ede-generic-scons-project)
62a81506 437 (ede-generic-new-autoloader "generic-cmake" "CMake"
cb85c0d8
EL
438 "CMakeLists" 'ede-generic-cmake-project)
439 )
440
441\f
442;;; SPECIFIC TYPES OF GENERIC BUILDS
443;;
444
445;;; MAKEFILE
446
447(defclass ede-generic-makefile-project (ede-generic-project)
448 ((buildfile :initform "Makefile")
449 )
450 "Generic Project for makefiles.")
451
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 ")
456 )
457
458
459;;; SCONS
460(defclass ede-generic-scons-project (ede-generic-project)
461 ((buildfile :initform "SConstruct")
462 )
463 "Generic Project for scons.")
464
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 ")
469 )
470
471
472;;; CMAKE
473(defclass ede-generic-cmake-project (ede-generic-project)
474 ((buildfile :initform "CMakeLists")
475 )
476 "Generic Project for cmake.")
477
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 ")
482 )
483
484(provide 'ede/generic)
485
486;; Local variables:
487;; generated-autoload-file: "loaddefs.el"
488;; generated-autoload-load-name: "ede/generic"
489;; End:
490
491;;; ede/generic.el ends here