summaryrefslogtreecommitdiff
path: root/lisp/cedet/ede/generic.el
blob: 3d1e1c5818ef88da7c338cc14b92e397d677f4e3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
;;; ede/generic.el --- Base Support for generic build systems

;; Copyright (C) 2010-2021 Free Software Foundation, Inc.

;; Author: Eric M. Ludlam <zappo@gnu.org>

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:
;;
;; There are a lot of build systems out there, and EDE can't support
;; them all fully.  The ede/generic.el system is the base for
;; supporting alternate build systems in a simple way, automatically.
;;
;; The structure is for the ede-generic baseclass, which is augmented
;; by simple sub-classes that can be created by users on an as needed
;; basis.  The generic system will have targets for many language
;; types, and create the targets on an as needed basis.  All
;; sub-project types will recycle the same generic target types.
;;
;; The generic target types will only be implemented for languages
;; where having EDE support actually matters, with a single MISC to
;; represent anything else.
;;
;; TOO MANY PROJECTS DETECTED:
;;
;; If enabling ede-generic support starts identifying too many
;; projects, drop a file called `.ede-ignore' into any directory where
;; you do not want a project to be.
;;
;; Customization:
;;
;; Since these projects are all so incredibly generic, a user will
;; need to configure some aspects of the project by hand.  In order to
;; enable this without configuring the project objects directly (which
;; are auto-generated) a special ede-generic-config object is defined to
;; hold the basics.  Generic projects will identify and use these
;; config files.
;;
;; Adding support for new projects:
;;
;; To add support to EDE Generic for new project types is very quick.
;; See the end of this file for examples such as CMake and SCons.
;;
;; Support consists of one class for your project, specifying the file
;; name used by the project system you want to support.  It also
;; should implement th method `ede-generic-setup-configuration' to
;; prepopulate the configurable portion of the generic project with
;; build details.
;;
;; Lastly, call `ede-generic-new-autoloader' to setup your project so
;; EDE can use it.
;;
;; Adding support for new types of source code:
;;
;; Sources of different types are supported with a simple class which
;; subclasses `ede-generic-target'.  The slots `shortname' and
;; `extension' should be given new initial values.
;;
;; Optionally, any target method used by EDE can then be overridden.
;; The ede-generic-target-c-cpp has some example methods setting up
;; the pre-processor map and system include path.
;;
;; NOTE: It is not necessary to modify ede/generic.el to add any of
;; the above described support features.

(require 'eieio-opt)
(require 'ede/config)
(require 'ede/shell)
(require 'semantic/db)

;;; Code:
;;
;; Start with the configuration system
(defclass ede-generic-config (ede-extra-config
			      ede-extra-config-build
			      ede-extra-config-program
			      ede-extra-config-c)
  ((file-header-line :initform ";; EDE Generic Project Configuration")
   )
  "User Configuration object for a generic project.")

(defun ede-generic-load (dir &optional rootproj)
  "Return a Generic Project object if there is a match.
Return nil if there isn't one.
Argument DIR is the directory it is created for.
ROOTPROJ is nil, since there is only one project."
  ;; Doesn't already exist, so let's make one.
  (let* ((alobj ede-constructing))
    (when (not alobj) (error "Cannot load generic project without the autoload instance"))
    ;;;
    ;; TODO - find the root dir.
    (let ((rootdir dir))
      (funcall (oref alobj class-sym)
	       (symbol-name (oref alobj class-sym))
	       :name (file-name-nondirectory (directory-file-name dir))
	       :version "1.0"
	       :directory (file-name-as-directory rootdir)
	       :file (expand-file-name (oref alobj proj-file)
				       rootdir)))
    ))

;;; Base Classes for the system
(defclass ede-generic-target (ede-target-with-config
			      ede-target-with-config-build
			      ede-target-with-config-program)
  ((shortname :initform ""
	     :type string
	     :allocation :class
	     :documentation
	     "Something prepended to the target name.")
  (extension :initform ""
	      :type string
	      :allocation :class
	      :documentation
	      "Regular expression representing the extension used for this target.
subclasses of this base target will override the default value.")
  )
  "Baseclass for all targets belonging to the generic ede system."
  :abstract t)

(defclass ede-generic-project (ede-project-with-config
			       ede-project-with-config-build
			       ede-project-with-config-program
			       ede-project-with-config-c
			       ede-project-with-config-java)
  ((config-class :initform ede-generic-config)
   (config-file-basename :initform "EDEConfig.el")
   (buildfile :initform ""
	      :type string
	      :allocation :class
	      :documentation "The file name that identifies a project of this type.
The class allocated value is replace by different sub classes.")
   )
  "The baseclass for all generic EDE project types."
  :abstract t)

(cl-defmethod initialize-instance ((this ede-generic-project)
				&rest fields)
  "Make sure the targets slot is bound."
  (cl-call-next-method)
  (unless (slot-boundp this 'targets)
    (oset this :targets nil))
  )

(cl-defmethod ede-project-root ((this ede-generic-project))
  "Return my root."
  this)

(cl-defmethod ede-find-subproject-for-directory ((proj ede-generic-project)
					      dir)
  "Return PROJ, for handling all subdirs below DIR."
  proj)

;;; A list of different targets
(defclass ede-generic-target-c-cpp (ede-generic-target
				    ede-target-with-config-c)
  ((shortname :initform "C/C++")
   (extension :initform "\\([ch]\\(pp\\|xx\\|\\+\\+\\)?\\|cc\\|hh\\|CC?\\)"))
  "EDE Generic Project target for C and C++ code.
All directories need at least one target.")

(defclass ede-generic-target-el (ede-generic-target)
  ((shortname :initform "ELisp")
   (extension :initform "el"))
  "EDE Generic Project target for Emacs Lisp code.
All directories need at least one target.")

(defclass ede-generic-target-fortran (ede-generic-target)
  ((shortname :initform "Fortran")
   (extension :initform "[fF]9[05]\\|[fF]\\|for"))
  "EDE Generic Project target for Fortran code.
All directories need at least one target.")

(defclass ede-generic-target-texi (ede-generic-target)
  ((shortname :initform "Texinfo")
   (extension :initform "texi"))
  "EDE Generic Project target for texinfo code.
All directories need at least one target.")

(defclass ede-generic-target-java (ede-generic-target
				   ede-target-with-config-java)
  ((shortname :initform "Java")
   (extension :initform "java"))
  "EDE Generic Project target for texinfo code.
All directories need at least one target.")

;; MISC must always be last since it will always match the file.
(defclass ede-generic-target-misc (ede-generic-target)
  ((shortname :initform "Misc")
   (extension :initform ""))
  "EDE Generic Project target for Misc files.
All directories need at least one target.")

;;; Automatic target acquisition.
(defun ede-generic-find-matching-target (class dir targets)
  "Find a target that is a CLASS and is in DIR in the list of TARGETS."
  (let ((match nil))
    (dolist (T targets)
      (when (and (object-of-class-p T class)
		 (string= (oref T path) dir))
	(setq match T)
      ))
    match))

(cl-defmethod ede-find-target ((proj ede-generic-project) buffer)
  "Find an EDE target in PROJ for BUFFER.
If one doesn't exist, create a new one for this directory."
  (let* ((ext (file-name-extension (buffer-file-name buffer)))
	 (classes (eieio-build-class-alist 'ede-generic-target t))
	 (cls nil)
	 (targets (oref proj targets))
	 (dir default-directory)
	 (ans nil)
	 )
    ;; Pick a matching class type.
    (when ext
      (dolist (C classes)
	(let* ((classsym (intern (car C)))
	       (extreg (oref-default classsym extension)))
	  (when (and (not (string= extreg ""))
		     (string-match (concat "\\`\\(?:" extreg "\\)\\'") ext))
	    (setq cls classsym)))))
    (when (not cls) (setq cls 'ede-generic-target-misc))
    ;; find a pre-existing matching target
    (setq ans (ede-generic-find-matching-target cls dir targets))
    ;; Create a new instance if there wasn't one
    (when (not ans)
      (setq ans (make-instance
		 cls
		 :name (oref-default cls shortname)
		 :path dir
		 :source nil))
      (object-add-to-list proj :targets ans)
      )
    ans))

;;; Creating Derived Projects:
;;
;; Derived projects need an autoloader so that EDE can find the
;; different projects on disk.
(defun ede-generic-new-autoloader (_internal-name external-name
                                                  projectfile class)
  "Add a new EDE Autoload instance for identifying a generic project.
INTERNAL-NAME is obsolete and ignored.
EXTERNAL-NAME is a human readable name to describe the project; it
must be unique among all autoloaded projects.
PROJECTFILE is a file name that identifies a project of this type to EDE, such
as a Makefile, or SConstruct file.
CLASS is the EIEIO class that is used to track this project.  It should subclass
`ede-generic-project'."
  (ede-add-project-autoload
   (ede-project-autoload :name external-name
			 :file 'ede/generic
			 :proj-file projectfile
			 :root-only nil
			 :load-type 'ede-generic-load
			 :class-sym class
			 :new-p nil
			 ;; NOTE: This project type is SAFE because it handles
			 ;; the user-query before loading its config file.  These
			 ;; project types are useful without the config file so
			 ;; do the safe part until the user creates a saved config
			 ;; file for it.
			 :safe-p t)
   ;; Generics must go at the end, since more specific types
   ;; can create Makefiles also.
   'generic))

;;;###autoload
(defun ede-enable-generic-projects ()
  "Enable generic project loaders."
  (interactive)
  (ede-generic-new-autoloader "generic-makefile" "Generic Make"
			      "Makefile" 'ede-generic-makefile-project)
  (ede-generic-new-autoloader "generic-scons" "Generic SCons"
			      "SConstruct" 'ede-generic-scons-project)
  (ede-generic-new-autoloader "generic-cmake" "Generic CMake"
			      "CMakeLists" 'ede-generic-cmake-project)

  ;; Super Generic found via revision control tags.
  (ede-generic-new-autoloader "generic-git" "Generic Git"
			      ".git" 'ede-generic-vc-project)
  (ede-generic-new-autoloader "generic-bzr" "Generic Bazaar"
			      ".bzr" 'ede-generic-vc-project)
  (ede-generic-new-autoloader "generic-hg" "Generic Mercurial"
			      ".hg" 'ede-generic-vc-project)
  (ede-generic-new-autoloader "generic-svn" "Generic Subversions"
			      ".svn" 'ede-generic-vc-project)
  (ede-generic-new-autoloader "generic-cvs" "Generic CVS"
			      "CVS" 'ede-generic-vc-project)
  (ede-generic-new-autoloader "generic-mtn" "Generic Monotone"
                              "_MTN" 'ede-generic-vc-project)

  ;; Take advantage of existing 'projectile' based projects.
  ;; @TODO - if projectile supports compile commands etc, can we
  ;; read that out?  Howto if projectile is not part of core emacs.
  (ede-generic-new-autoloader "generic-projectile" "Generic .projectile"
			      ".projectile" 'ede-generic-vc-project)

  )


;;; SPECIFIC TYPES OF GENERIC BUILDS
;;

;;; MAKEFILE

(defclass ede-generic-makefile-project (ede-generic-project)
  ((buildfile :initform "Makefile")
   )
  "Generic Project for makefiles.")

(cl-defmethod ede-generic-setup-configuration ((proj ede-generic-makefile-project) config)
  "Setup a configuration for Make."
  (oset config build-command "make -k")
  (oset config debug-command "gdb ")
  )


;;; SCONS
(defclass ede-generic-scons-project (ede-generic-project)
  ((buildfile :initform "SConstruct")
   )
  "Generic Project for scons.")

(cl-defmethod ede-generic-setup-configuration ((proj ede-generic-scons-project) config)
  "Setup a configuration for SCONS."
  (oset config build-command "scons")
  (oset config debug-command "gdb ")
  )


;;; CMAKE
(defclass ede-generic-cmake-project (ede-generic-project)
  ((buildfile :initform "CMakeLists")
   )
  "Generic Project for cmake.")

(cl-defmethod ede-generic-setup-configuration ((proj ede-generic-cmake-project) config)
  "Setup a configuration for CMake."
  (oset config build-command "cmake")
  (oset config debug-command "gdb ")
  )

;;; Generic Version Control System
(defclass ede-generic-vc-project (ede-generic-project)
  ()
  "Generic project found via Version Control files.")

(cl-defmethod ede-generic-setup-configuration ((proj ede-generic-vc-project) config)
  "Setup a configuration for projects identified by revision control."
  )

(provide 'ede/generic)

;; Local variables:
;; generated-autoload-file: "loaddefs.el"
;; generated-autoload-load-name: "ede/generic"
;; End:

;;; ede/generic.el ends here