Convert consecutive FSF copyright years to ranges.
[bpt/emacs.git] / lisp / cedet / ede / generic.el
CommitLineData
cb85c0d8
EL
1;;; ede/generic.el --- Base Support for generic build systems
2
73b0cd50 3;; Copyright (C) 2010-2011 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
25;; them all fully. The ede-generic.el system is the base for
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;;
46;; Since these projects are all so increadibly 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-confg object is defined to
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;;
73;; Optionally, any target method used by EDE can then be overriden.
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)
82(require 'semantic/db)
83
84;;; Code:
85;;
86;; Start with the configuration system
87(defclass ede-generic-config (eieio-persistent)
88 ((extension :initform ".ede")
89 (file-header-line :initform ";; EDE Generic Project Configuration")
90 (project :initform nil
91 :documentation
92 "The project this config is bound to.")
93 ;; Generic customizations
94 (build-command :initarg :build-command
95 :initform "make -k"
96 :type string
97 :custom string
98 :group (default build)
99 :documentation
100 "Command used for building this project.")
101 (debug-command :initarg :debug-command
102 :initform "gdb "
103 :type string
104 :custom string
105 :group (default build)
106 :documentation
107 "Command used for debugging this project.")
108 ;; C target customixations
109 (c-include-path :initarg :c-include-path
110 :initform nil
111 :type list
112 :custom (repeat (string :tag "Path"))
113 :group c
114 :documentation
115 "The include path used by C/C++ projects.")
116 (c-preprocessor-table :initarg :c-preprocessor-table
117 :initform nil
118 :type list
119 :custom (repeat (cons (string :tag "Macro")
120 (string :tag "Value")))
121 :group c
122 :documentation
123 "Preprocessor Symbols for this project.")
124 (c-preprocessor-files :initarg :c-preprocessor-files
125 :initform nil
126 :type list
127 :custom (repeat (string :tag "Include File")))
128 )
129 "User Configuration object for a generic project.")
130
131(defun ede-generic-load (dir &optional rootproj)
132 "Return a Generic Project object if there is a match.
133Return nil if there isn't one.
134Argument DIR is the directory it is created for.
135ROOTPROJ is nil, since there is only one project."
136 ;; Doesn't already exist, so lets make one.
137 (let* ((alobj ede-constructing)
138 (this nil))
139 (when (not alobj) (error "Cannot load generic project without the autoload instance"))
140
141 (setq this
142 (funcall (oref alobj class-sym)
143 (symbol-name (oref alobj class-sym))
144 :name (file-name-nondirectory
145 (directory-file-name dir))
146 :version "1.0"
147 :directory (file-name-as-directory dir)
148 :file (expand-file-name (oref alobj :proj-file)) ))
149 (ede-add-project-to-global-list this)
150 ))
151
152;;; Base Classes for the system
153(defclass ede-generic-target (ede-target)
154 ((shortname :initform ""
155 :type string
156 :allocation :class
157 :documentation
158 "Something prepended to the target name.")
159 (extension :initform ""
160 :type string
161 :allocation :class
162 :documentation
163 "Regular expression representing the extension used for this target.
164subclasses of this base target will override the default value.")
165 )
166 "Baseclass for all targets belonging to the generic ede system."
167 :abstract t)
168
169(defclass ede-generic-project (ede-project)
170 ((buildfile :initform ""
171 :type string
172 :allocation :class
173 :documentation "The file name that identifies a project of this type.
174The class allocated value is replace by different sub classes.")
175 (config :initform nil
176 :type (or null ede-generic-config)
177 :documentation
178 "The configuration object for this project.")
179 )
180 "The baseclass for all generic EDE project types."
181 :abstract t)
182
183(defmethod initialize-instance ((this ede-generic-project)
184 &rest fields)
185 "Make sure the targets slot is bound."
186 (call-next-method)
187 (unless (slot-boundp this 'targets)
188 (oset this :targets nil))
189 )
190
191(defmethod ede-generic-get-configuration ((proj ede-generic-project))
192 "Return the configuration for the project PROJ."
193 (let ((config (oref proj config)))
194 (when (not config)
195 (let ((fname (expand-file-name "EDEConfig.el"
196 (oref proj :directory))))
197 (if (file-exists-p fname)
198 ;; Load in the configuration
199 (setq config (eieio-persistent-read fname))
200 ;; Create a new one.
201 (setq config (ede-generic-config
202 "Configuration"
203 :file fname))
204 ;; Set initial values based on project.
205 (ede-generic-setup-configuration proj config))
206 ;; Link things together.
207 (oset proj config config)
208 (oset config project proj)))
209 config))
210
211(defmethod ede-generic-setup-configuration ((proj ede-generic-project) config)
212 "Default configuration setup method."
213 nil)
214
215(defmethod ede-commit-project ((proj ede-generic-project))
216 "Commit any change to PROJ to its file."
217 (let ((config (ede-generic-get-configuration proj)))
218 (ede-commit config)))
219
220;;; A list of different targets
221(defclass ede-generic-target-c-cpp (ede-generic-target)
222 ((shortname :initform "C/C++")
223 (extension :initform "\\([ch]\\(pp\\|xx\\|\\+\\+\\)?\\|cc\\|hh\\|CC?\\)"))
224 "EDE Generic Project target for C and C++ code.
225All directories need at least one target.")
226
227(defclass ede-generic-target-el (ede-generic-target)
228 ((shortname :initform "ELisp")
229 (extension :initform "el"))
230 "EDE Generic Project target for Emacs Lisp code.
231All directories need at least one target.")
232
233(defclass ede-generic-target-fortran (ede-generic-target)
234 ((shortname :initform "Fortran")
235 (extension :initform "[fF]9[05]\\|[fF]\\|for"))
236 "EDE Generic Project target for Fortran code.
237All directories need at least one target.")
238
239(defclass ede-generic-target-texi (ede-generic-target)
240 ((shortname :initform "Texinfo")
241 (extension :initform "texi"))
242 "EDE Generic Project target for texinfo code.
243All directories need at least one target.")
244
245;; MISC must always be last since it will always match the file.
246(defclass ede-generic-target-misc (ede-generic-target)
247 ((shortname :initform "Misc")
248 (extension :initform ""))
249 "EDE Generic Project target for Misc files.
250All directories need at least one target.")
251
252;;; Automatic target aquisition.
253(defun ede-generic-find-matching-target (class dir targets)
254 "Find a target that is a CLASS and is in DIR in the list of TARGETS."
255 (let ((match nil))
256 (dolist (T targets)
257 (when (and (object-of-class-p T class)
258 (string= (oref T :path) dir))
259 (setq match T)
260 ))
261 match))
262
263(defmethod ede-find-target ((proj ede-generic-project) buffer)
264 "Find an EDE target in PROJ for BUFFER.
265If one doesn't exist, create a new one for this directory."
266 (let* ((ext (file-name-extension (buffer-file-name buffer)))
267 (classes (eieio-build-class-alist 'ede-generic-target t))
268 (cls nil)
269 (targets (oref proj targets))
270 (dir default-directory)
271 (ans nil)
272 )
273 ;; Pick a matching class type.
274 (when ext
275 (dolist (C classes)
276 (let* ((classsym (intern (car C)))
277 (extreg (oref classsym extension)))
278 (when (and (not (string= extreg ""))
279 (string-match (concat "^" extreg "$") ext))
280 (setq cls classsym)))))
281 (when (not cls) (setq cls 'ede-generic-target-misc))
282 ;; find a pre-existing matching target
283 (setq ans (ede-generic-find-matching-target cls dir targets))
284 ;; Create a new instance if there wasn't one
285 (when (not ans)
286 (setq ans (make-instance
287 cls
288 :name (oref cls shortname)
289 :path dir
290 :source nil))
291 (object-add-to-list proj :targets ans)
292 )
293 ans))
294
295;;; C/C++ support
296(defmethod ede-preprocessor-map ((this ede-generic-target-c-cpp))
297 "Get the pre-processor map for some generic C code."
298 (let* ((proj (ede-target-parent this))
299 (root (ede-project-root proj))
300 (config (ede-generic-get-configuration proj))
301 filemap
302 )
303 ;; Preprocessor files
304 (dolist (G (oref config :c-preprocessor-files))
305 (let ((table (semanticdb-file-table-object
306 (ede-expand-filename root G))))
307 (when table
308 (when (semanticdb-needs-refresh-p table)
309 (semanticdb-refresh-table table))
310 (setq filemap (append filemap (oref table lexical-table)))
311 )))
312 ;; The core table
313 (setq filemap (append filemap (oref config :c-preprocessor-table)))
314
315 filemap
316 ))
317
318(defmethod ede-system-include-path ((this ede-generic-target-c-cpp))
319 "Get the system include path used by project THIS."
320 (let* ((proj (ede-target-parent this))
321 (config (ede-generic-get-configuration proj)))
322 (oref config c-include-path)))
323
324;;; Customization
325;;
326(defmethod ede-customize ((proj ede-generic-project))
327 "Customize the EDE project PROJ."
328 (let ((config (ede-generic-get-configuration proj)))
329 (eieio-customize-object config)))
330
331(defmethod ede-customize ((target ede-generic-target))
332 "Customize the EDE TARGET."
333 ;; Nothing unique for the targets, use the project.
334 (ede-customize-project))
335
336(defmethod eieio-done-customizing ((config ede-generic-config))
337 "Called when EIEIO is done customizing the configuration object.
338We need to go back through the old buffers, and update them with
339the new configuration."
340 (ede-commit config)
341 ;; Loop over all the open buffers, and re-apply.
342 (ede-map-targets
343 (oref config project)
344 (lambda (target)
345 (ede-map-target-buffers
346 target
347 (lambda (b)
348 (with-current-buffer b
349 (ede-apply-target-options)))))))
350
351(defmethod ede-commit ((config ede-generic-config))
352 "Commit all changes to the configuration to disk."
353 (eieio-persistent-save config))
354
355;;; Creating Derived Projects:
356;;
357;; Derived projects need an autoloader so that EDE can find the
358;; different projects on disk.
359(defun ede-generic-new-autoloader (internal-name external-name
360 projectfile class)
361 "Add a new EDE Autoload instance for identifying a generic project.
362INTERNAL-NAME is a long name that identifies thsi project type.
363EXTERNAL-NAME is a shorter human readable name to describe the project.
364PROJECTFILE is a file name that identifies a project of this type to EDE, such as
365a Makefile, or SConstruct file.
366CLASS is the EIEIO class that is used to track this project. It should subclass
367the class `ede-generic-project' project."
368 (add-to-list 'ede-project-class-files
369 (ede-project-autoload internal-name
370 :name external-name
b93e37e5 371 :file 'ede/generic
cb85c0d8
EL
372 :proj-file projectfile
373 :load-type 'ede-generic-load
374 :class-sym class
375 :new-p nil)
376 ;; Generics must go at the end, since more specific types
377 ;; can create Makefiles also.
378 t))
379
380;;;###autoload
381(defun ede-enable-generic-projects ()
382 "Enable generic project loaders."
383 (interactive)
384 (ede-generic-new-autoloader "edeproject-makefile" "Make"
385 "Makefile" 'ede-generic-makefile-project)
386 (ede-generic-new-autoloader "edeproject-scons" "SCons"
387 "SConstruct" 'ede-generic-scons-project)
388 (ede-generic-new-autoloader "edeproject-cmake" "CMake"
389 "CMakeLists" 'ede-generic-cmake-project)
390 )
391
392\f
393;;; SPECIFIC TYPES OF GENERIC BUILDS
394;;
395
396;;; MAKEFILE
397
398(defclass ede-generic-makefile-project (ede-generic-project)
399 ((buildfile :initform "Makefile")
400 )
401 "Generic Project for makefiles.")
402
403(defmethod ede-generic-setup-configuration ((proj ede-generic-makefile-project) config)
404 "Setup a configuration for Make."
405 (oset config build-command "make -k")
406 (oset config debug-command "gdb ")
407 )
408
409
410;;; SCONS
411(defclass ede-generic-scons-project (ede-generic-project)
412 ((buildfile :initform "SConstruct")
413 )
414 "Generic Project for scons.")
415
416(defmethod ede-generic-setup-configuration ((proj ede-generic-scons-project) config)
417 "Setup a configuration for SCONS."
418 (oset config build-command "scons")
419 (oset config debug-command "gdb ")
420 )
421
422
423;;; CMAKE
424(defclass ede-generic-cmake-project (ede-generic-project)
425 ((buildfile :initform "CMakeLists")
426 )
427 "Generic Project for cmake.")
428
429(defmethod ede-generic-setup-configuration ((proj ede-generic-cmake-project) config)
430 "Setup a configuration for CMake."
431 (oset config build-command "cmake")
432 (oset config debug-command "gdb ")
433 )
434
435(provide 'ede/generic)
436
437;; Local variables:
438;; generated-autoload-file: "loaddefs.el"
439;; generated-autoload-load-name: "ede/generic"
440;; End:
441
442;;; ede/generic.el ends here