]> code.delx.au - gnu-emacs/blob - lisp/cedet/ede/base.el
Merge from emacs-24; up to 2012-12-27T17:59:21Z!rgm@gnu.org
[gnu-emacs] / lisp / cedet / ede / base.el
1 ;;; ede/base.el --- Baseclasses for EDE.
2
3 ;; Copyright (C) 2010-2013 Free Software Foundation, Inc.
4
5 ;; Author: Eric M. Ludlam <zappo@gnu.org>
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 ;; Baseclasses for EDE.
25 ;;
26 ;; Contains all the base structures needed by EDE.
27
28 ;;; Code:
29 (require 'eieio)
30 (require 'eieio-speedbar)
31 (require 'ede/auto)
32
33 ;; Defined in ede.el:
34 (defvar ede-projects)
35 (defvar ede-object)
36 (defvar ede-object-root-project)
37
38 (declare-function data-debug-new-buffer "data-debug")
39 (declare-function data-debug-insert-object-slots "eieio-datadebug")
40 (declare-function ede-parent-project "ede" (&optional obj))
41 (declare-function ede-current-project "ede" (&optional dir))
42
43 ;;; TARGET
44 ;;
45 ;; The TARGET is an entity in a project that knows about files
46 ;; and features of those files.
47
48 (defclass ede-target (eieio-speedbar-directory-button)
49 ((buttonface :initform speedbar-file-face) ;override for superclass
50 (name :initarg :name
51 :type string
52 :custom string
53 :label "Name"
54 :group (default name)
55 :documentation "Name of this target.")
56 ;; @todo - I think this should be "dir", and not "path".
57 (path :initarg :path
58 :type string
59 ;:custom string
60 ;:label "Path to target"
61 ;:group (default name)
62 :documentation "The path to the sources of this target.
63 Relative to the path of the project it belongs to.")
64 (source :initarg :source
65 :initform nil
66 ;; I'd prefer a list of strings.
67 :type list
68 :custom (repeat (string :tag "File"))
69 :label "Source Files"
70 :group (default source)
71 :documentation "Source files in this target.")
72 (versionsource :initarg :versionsource
73 :initform nil
74 :type list
75 :custom (repeat (string :tag "File"))
76 :label "Source Files with Version String"
77 :group (source)
78 :documentation
79 "Source files with a version string in them.
80 These files are checked for a version string whenever the EDE version
81 of the master project is changed. When strings are found, the version
82 previously there is updated.")
83 ;; Class level slots
84 ;;
85 (sourcetype :allocation :class
86 :type list ;; list of symbols
87 :documentation
88 "A list of `ede-sourcecode' objects this class will handle.
89 This is used to match target objects with the compilers they can use, and
90 which files this object is interested in."
91 :accessor ede-object-sourcecode)
92 (keybindings :allocation :class
93 :initform (("D" . ede-debug-target))
94 :documentation
95 "Keybindings specialized to this type of target."
96 :accessor ede-object-keybindings)
97 (menu :allocation :class
98 :initform ( [ "Debug target" ede-debug-target
99 (ede-buffer-belongs-to-target-p) ]
100 [ "Run target" ede-run-target
101 (ede-buffer-belongs-to-target-p) ]
102 )
103 :documentation "Menu specialized to this type of target."
104 :accessor ede-object-menu)
105 )
106 "A target is a structure that describes a file set that produces something.
107 Targets, as with 'Make', is an entity that will manage a file set
108 and knows how to compile or otherwise transform those files into some
109 other desired outcome.")
110
111 ;;; PROJECT/PLACEHOLDER
112 ;;
113 ;; Project placeholders are minimum parts of a project used
114 ;; by the project cache. The project cache can refer to these placeholders,
115 ;; and swap them out with the real-deal when that project is loaded.
116 ;;
117 (defclass ede-project-placeholder (eieio-speedbar-directory-button)
118 ((name :initarg :name
119 :initform "Untitled"
120 :type string
121 :custom string
122 :label "Name"
123 :group (default name)
124 :documentation "The name used when generating distribution files.")
125 (version :initarg :version
126 :initform "1.0"
127 :type string
128 :custom string
129 :label "Version"
130 :group (default name)
131 :documentation "The version number used when distributing files.")
132 (directory :type string
133 :initarg :directory
134 :documentation "Directory this project is associated with.")
135 (dirinode :documentation "The inode id for :directory.")
136 (file :type string
137 :initarg :file
138 :documentation "The File uniquely tagging this project instance.
139 For some project types, this will be the file that stores the project configuration.
140 In other projects types, this file is merely a unique identifier to this type of project.")
141 (rootproject ; :initarg - no initarg, don't save this slot!
142 :initform nil
143 :type (or null ede-project-placeholder-child)
144 :documentation "Pointer to our root project.")
145 )
146 "Placeholder object for projects not loaded into memory.
147 Projects placeholders will be stored in a user specific location
148 and querying them will cause the actual project to get loaded.")
149
150 ;;; PROJECT
151 ;;
152 ;; An EDE project controls a set of TARGETS, and can also contain
153 ;; multiple SUBPROJECTS.
154 ;;
155 ;; The project defines a set of features that need to be built from
156 ;; files, in addition as to controlling what to do with the file set,
157 ;; such as creating distributions, compilation, and web sites.
158 ;;
159 ;; Projects can also affect how EDE works, by changing what appears in
160 ;; the EDE menu, or how some keys are bound.
161 ;;
162 (defclass ede-project (ede-project-placeholder)
163 ((subproj :initform nil
164 :type list
165 :documentation "Sub projects controlled by this project.
166 For Automake based projects, each directory is treated as a project.")
167 (targets :initarg :targets
168 :type ede-target-list
169 :custom (repeat (object :objectcreatefcn ede-new-target-custom))
170 :label "Local Targets"
171 :group (targets)
172 :documentation "List of top level targets in this project.")
173 (locate-obj :type (or null ede-locate-base-child)
174 :documentation
175 "A locate object to use as a backup to `ede-expand-filename'.")
176 (tool-cache :initarg :tool-cache
177 :type list
178 :custom (repeat object)
179 :label "Tool: "
180 :group tools
181 :documentation "List of tool cache configurations in this project.
182 This allows any tool to create, manage, and persist project-specific settings.")
183 (mailinglist :initarg :mailinglist
184 :initform ""
185 :type string
186 :custom string
187 :label "Mailing List Address"
188 :group name
189 :documentation
190 "An email address where users might send email for help.")
191 (web-site-url :initarg :web-site-url
192 :initform ""
193 :type string
194 :custom string
195 :label "Web Site URL"
196 :group name
197 :documentation "URL to this projects web site.
198 This is a URL to be sent to a web site for documentation.")
199 (web-site-directory :initarg :web-site-directory
200 :initform ""
201 :custom string
202 :label "Web Page Directory"
203 :group name
204 :documentation
205 "A directory where web pages can be found by Emacs.
206 For remote locations use a path compatible with ange-ftp or EFS.
207 You can also use TRAMP for use with rcp & scp.")
208 (web-site-file :initarg :web-site-file
209 :initform ""
210 :custom string
211 :label "Web Page File"
212 :group name
213 :documentation
214 "A file which contains the home page for this project.
215 This file can be relative to slot `web-site-directory'.
216 This can be a local file, use ange-ftp, EFS, or TRAMP.")
217 (ftp-site :initarg :ftp-site
218 :initform ""
219 :type string
220 :custom string
221 :label "FTP site"
222 :group name
223 :documentation
224 "FTP site where this project's distribution can be found.
225 This FTP site should be in Emacs form, as needed by `ange-ftp', but can
226 also be of a form used by TRAMP for use with scp, or rcp.")
227 (ftp-upload-site :initarg :ftp-upload-site
228 :initform ""
229 :type string
230 :custom string
231 :label "FTP Upload site"
232 :group name
233 :documentation
234 "FTP Site to upload new distributions to.
235 This FTP site should be in Emacs form as needed by `ange-ftp'.
236 If this slot is nil, then use `ftp-site' instead.")
237 (configurations :initarg :configurations
238 :initform ("debug" "release")
239 :type list
240 :custom (repeat string)
241 :label "Configuration Options"
242 :group (settings)
243 :documentation "List of available configuration types.
244 Individual target/project types can form associations between a configuration,
245 and target specific elements such as build variables.")
246 (configuration-default :initarg :configuration-default
247 :initform "debug"
248 :custom string
249 :label "Current Configuration"
250 :group (settings)
251 :documentation "The default configuration.")
252 (local-variables :initarg :local-variables
253 :initform nil
254 :custom (repeat (cons (sexp :tag "Variable")
255 (sexp :tag "Value")))
256 :label "Project Local Variables"
257 :group (settings)
258 :documentation "Project local variables")
259 (keybindings :allocation :class
260 :initform (("D" . ede-debug-target)
261 ("R" . ede-run-target))
262 :documentation "Keybindings specialized to this type of target."
263 :accessor ede-object-keybindings)
264 (menu :allocation :class
265 :initform
266 (
267 [ "Update Version" ede-update-version ede-object ]
268 [ "Version Control Status" ede-vc-project-directory ede-object ]
269 [ "Edit Project Homepage" ede-edit-web-page
270 (and ede-object (oref (ede-toplevel) web-site-file)) ]
271 [ "Browse Project URL" ede-web-browse-home
272 (and ede-object
273 (not (string= "" (oref (ede-toplevel) web-site-url)))) ]
274 "--"
275 [ "Rescan Project Files" ede-rescan-toplevel t ]
276 [ "Edit Projectfile" ede-edit-file-target
277 (ede-buffer-belongs-to-project-p) ]
278 )
279 :documentation "Menu specialized to this type of target."
280 :accessor ede-object-menu)
281 )
282 "Top level EDE project specification.
283 All specific project types must derive from this project."
284 :method-invocation-order :depth-first)
285
286 ;;; Important macros for doing commands.
287 ;;
288 (defmacro ede-with-projectfile (obj &rest forms)
289 "For the project in which OBJ resides, execute FORMS."
290 `(save-window-excursion
291 (let* ((pf (if (obj-of-class-p ,obj ede-target)
292 (ede-target-parent ,obj)
293 ,obj))
294 (dbka (get-file-buffer (oref pf file))))
295 (if (not dbka) (find-file (oref pf file))
296 (switch-to-buffer dbka))
297 ,@forms
298 (if (not dbka) (kill-buffer (current-buffer))))))
299 (put 'ede-with-projectfile 'lisp-indent-function 1)
300
301 ;;; The EDE persistent cache.
302 ;;
303 ;; The cache is a way to mark where all known projects live without
304 ;; loading those projects into memory, or scanning for them each time
305 ;; emacs starts.
306 ;;
307 (defcustom ede-project-placeholder-cache-file
308 (locate-user-emacs-file "ede-projects.el" ".projects.ede")
309 "File containing the list of projects EDE has viewed."
310 :group 'ede
311 :type 'file)
312
313 (defvar ede-project-cache-files nil
314 "List of project files EDE has seen before.")
315
316 (defun ede-save-cache ()
317 "Save a cache of EDE objects that Emacs has seen before."
318 (interactive)
319 (let ((p ede-projects)
320 (c ede-project-cache-files)
321 (recentf-exclude '( (lambda (f) t) ))
322 )
323 (condition-case nil
324 (progn
325 (set-buffer (find-file-noselect ede-project-placeholder-cache-file t))
326 (erase-buffer)
327 (insert ";; EDE project cache file.
328 ;; This contains a list of projects you have visited.\n(")
329 (while p
330 (when (and (car p) (ede-project-p p))
331 (let ((f (oref (car p) file)))
332 (when (file-exists-p f)
333 (insert "\n \"" f "\""))))
334 (setq p (cdr p)))
335 (while c
336 (insert "\n \"" (car c) "\"")
337 (setq c (cdr c)))
338 (insert "\n)\n")
339 (condition-case nil
340 (save-buffer 0)
341 (error
342 (message "File %s could not be saved."
343 ede-project-placeholder-cache-file)))
344 (kill-buffer (current-buffer))
345 )
346 (error
347 (message "File %s could not be read."
348 ede-project-placeholder-cache-file))
349
350 )))
351
352 (defun ede-load-cache ()
353 "Load the cache of EDE projects."
354 (save-excursion
355 (let ((cachebuffer (get-buffer-create "*ede cache*")))
356 (condition-case nil
357 (with-current-buffer cachebuffer
358 (erase-buffer)
359 (when (file-exists-p ede-project-placeholder-cache-file)
360 (insert-file-contents ede-project-placeholder-cache-file))
361 (goto-char (point-min))
362 (let ((c (read (current-buffer)))
363 (new nil)
364 (p ede-projects))
365 ;; Remove loaded projects from the cache.
366 (while p
367 (setq c (delete (oref (car p) file) c))
368 (setq p (cdr p)))
369 ;; Remove projects that aren't on the filesystem
370 ;; anymore.
371 (while c
372 (when (file-exists-p (car c))
373 (setq new (cons (car c) new)))
374 (setq c (cdr c)))
375 ;; Save it
376 (setq ede-project-cache-files (nreverse new))))
377 (error nil))
378 (when cachebuffer (kill-buffer cachebuffer))
379 )))
380
381 ;;; Get the cache usable.
382
383 ;; @TODO - Remove this cache setup, or use this for something helpful.
384 ;;(add-hook 'kill-emacs-hook 'ede-save-cache)
385 ;;(when (not noninteractive)
386 ;; ;; No need to load the EDE cache if we aren't interactive.
387 ;; ;; This occurs during batch byte-compiling of other tools.
388 ;; (ede-load-cache))
389
390 \f
391 ;;; METHODS
392 ;;
393 ;; The methods in ede-base handle project related behavior, and DO NOT
394 ;; related to EDE mode commands directory, such as keybindings.
395 ;;
396 ;; Mode related methods are in ede.el. These methods are related
397 ;; project specific activities not directly tied to a keybinding.
398 (defmethod ede-subproject-relative-path ((proj ede-project) &optional parent-in)
399 "Get a path name for PROJ which is relative to the parent project.
400 If PARENT is specified, then be relative to the PARENT project.
401 Specifying PARENT is useful for sub-sub projects relative to the root project."
402 (let* ((parent (or parent-in (ede-parent-project proj)))
403 (dir (file-name-directory (oref proj file))))
404 (if (and parent (not (eq parent proj)))
405 (file-relative-name dir (file-name-directory (oref parent file)))
406 "")))
407
408 (defmethod ede-subproject-p ((proj ede-project))
409 "Return non-nil if PROJ is a sub project."
410 ;; @TODO - Use this in more places, and also pay attention to
411 ;; metasubproject in ede/proj.el
412 (ede-parent-project proj))
413
414 \f
415 ;;; Default descriptive methods for EDE classes
416 ;;
417 ;; These are methods which you might want to override, but there is
418 ;; no need to in most situations because they are either a) simple, or
419 ;; b) cosmetic.
420
421 (defmethod ede-name ((this ede-target))
422 "Return the name of THIS target."
423 (oref this name))
424
425 (defmethod ede-target-name ((this ede-target))
426 "Return the name of THIS target, suitable for make or debug style commands."
427 (oref this name))
428
429 (defmethod ede-name ((this ede-project))
430 "Return a short-name for THIS project file.
431 Do this by extracting the lowest directory name."
432 (oref this name))
433
434 (defmethod ede-description ((this ede-project))
435 "Return a description suitable for the minibuffer about THIS."
436 (format "Project %s: %d subprojects, %d targets."
437 (ede-name this) (length (oref this subproj))
438 (length (oref this targets))))
439
440 (defmethod ede-description ((this ede-target))
441 "Return a description suitable for the minibuffer about THIS."
442 (format "Target %s: with %d source files."
443 (ede-name this) (length (oref this source))))
444
445 ;;; HEADERS/DOC
446 ;;
447 ;; Targets and projects are often associated with other files, such as
448 ;; header files, documentation files and the like. Have strong
449 ;; associations can make useful user commands to quickly navigate
450 ;; between the files based on their associations.
451 ;;
452 (defun ede-header-file ()
453 "Return the header file for the current buffer.
454 Not all buffers need headers, so return nil if no applicable."
455 (if ede-object
456 (ede-buffer-header-file ede-object (current-buffer))
457 nil))
458
459 (defmethod ede-buffer-header-file ((this ede-project) buffer)
460 "Return nil, projects don't have header files."
461 nil)
462
463 (defmethod ede-buffer-header-file ((this ede-target) buffer)
464 "There are no default header files in EDE.
465 Do a quick check to see if there is a Header tag in this buffer."
466 (with-current-buffer buffer
467 (if (re-search-forward "::Header:: \\([a-zA-Z0-9.]+\\)" nil t)
468 (buffer-substring-no-properties (match-beginning 1)
469 (match-end 1))
470 (let ((src (ede-target-sourcecode this))
471 (found nil))
472 (while (and src (not found))
473 (setq found (ede-buffer-header-file (car src) (buffer-file-name))
474 src (cdr src)))
475 found))))
476
477 (defun ede-documentation-files ()
478 "Return the documentation files for the current buffer.
479 Not all buffers need documentations, so return nil if no applicable.
480 Some projects may have multiple documentation files, so return a list."
481 (if ede-object
482 (ede-buffer-documentation-files ede-object (current-buffer))
483 nil))
484
485 (defmethod ede-buffer-documentation-files ((this ede-project) buffer)
486 "Return all documentation in project THIS based on BUFFER."
487 ;; Find the info node.
488 (ede-documentation this))
489
490 (defmethod ede-buffer-documentation-files ((this ede-target) buffer)
491 "Check for some documentation files for THIS.
492 Also do a quick check to see if there is a Documentation tag in this BUFFER."
493 (with-current-buffer buffer
494 (if (re-search-forward "::Documentation:: \\([a-zA-Z0-9.]+\\)" nil t)
495 (buffer-substring-no-properties (match-beginning 1)
496 (match-end 1))
497 ;; Check the master project
498 (let ((cp (ede-toplevel)))
499 (ede-buffer-documentation-files cp (current-buffer))))))
500
501 (defmethod ede-documentation ((this ede-project))
502 "Return a list of files that provide documentation.
503 Documentation is not for object THIS, but is provided by THIS for other
504 files in the project."
505 (let ((targ (oref this targets))
506 (proj (oref this subproj))
507 (found nil))
508 (while targ
509 (setq found (append (ede-documentation (car targ)) found)
510 targ (cdr targ)))
511 (while proj
512 (setq found (append (ede-documentation (car proj)) found)
513 proj (cdr proj)))
514 found))
515
516 (defmethod ede-documentation ((this ede-target))
517 "Return a list of files that provide documentation.
518 Documentation is not for object THIS, but is provided by THIS for other
519 files in the project."
520 nil)
521
522 (defun ede-html-documentation-files ()
523 "Return a list of HTML documentation files associated with this project."
524 (ede-html-documentation (ede-toplevel))
525 )
526
527 (defmethod ede-html-documentation ((this ede-project))
528 "Return a list of HTML files provided by project THIS."
529
530 )
531
532 ;;; Default "WANT" methods.
533 ;;
534 ;; These methods are used to determine if a target "wants", or could
535 ;; somehow handle a file, or some source type.
536 ;;
537 (defmethod ede-want-file-p ((this ede-target) file)
538 "Return non-nil if THIS target wants FILE."
539 ;; By default, all targets reference the source object, and let it decide.
540 (let ((src (ede-target-sourcecode this)))
541 (while (and src (not (ede-want-file-p (car src) file)))
542 (setq src (cdr src)))
543 src))
544
545 (defmethod ede-want-file-source-p ((this ede-target) file)
546 "Return non-nil if THIS target wants FILE."
547 ;; By default, all targets reference the source object, and let it decide.
548 (let ((src (ede-target-sourcecode this)))
549 (while (and src (not (ede-want-file-source-p (car src) file)))
550 (setq src (cdr src)))
551 src))
552
553 (defmethod ede-target-sourcecode ((this ede-target))
554 "Return the sourcecode objects which THIS permits."
555 (let ((sc (oref this sourcetype))
556 (rs nil))
557 (while (and (listp sc) sc)
558 (setq rs (cons (symbol-value (car sc)) rs)
559 sc (cdr sc)))
560 rs))
561
562 \f
563 ;;; Debugging.
564 ;;
565 (defun ede-adebug-project ()
566 "Run adebug against the current EDE project.
567 Display the results as a debug list."
568 (interactive)
569 (require 'data-debug)
570 (when (ede-current-project)
571 (data-debug-new-buffer "*Analyzer ADEBUG*")
572 (data-debug-insert-object-slots (ede-current-project) "")
573 ))
574
575 (defun ede-adebug-project-parent ()
576 "Run adebug against the current EDE parent project.
577 Display the results as a debug list."
578 (interactive)
579 (require 'data-debug)
580 (when (ede-parent-project)
581 (data-debug-new-buffer "*Analyzer ADEBUG*")
582 (data-debug-insert-object-slots (ede-parent-project) "")
583 ))
584
585 (defun ede-adebug-project-root ()
586 "Run adebug against the current EDE parent project.
587 Display the results as a debug list."
588 (interactive)
589 (require 'data-debug)
590 (when (ede-toplevel)
591 (data-debug-new-buffer "*Analyzer ADEBUG*")
592 (data-debug-insert-object-slots (ede-toplevel) "")
593 ))
594
595 \f
596
597 ;;; TOPLEVEL PROJECT
598 ;;
599 ;; The toplevel project is a way to identify the EDE structure that belongs
600 ;; to the top of a project.
601
602 (defun ede-toplevel (&optional subproj)
603 "Return the ede project which is the root of the current project.
604 Optional argument SUBPROJ indicates a subproject to start from
605 instead of the current project."
606 (or ede-object-root-project
607 (let* ((cp (or subproj (ede-current-project))))
608 (or (and cp (ede-project-root cp))
609 (progn
610 (while (ede-parent-project cp)
611 (setq cp (ede-parent-project cp)))
612 cp)))))
613
614 \f
615 ;;; Utility functions
616 ;;
617
618 (defun ede-normalize-file/directory (this project-file-name)
619 "Fills :directory or :file slots if they're missing in project THIS.
620 The other slot will be used to calculate values.
621 PROJECT-FILE-NAME is a name of project file (short name, like 'pom.xml', etc."
622 (when (and (or (not (slot-boundp this :file))
623 (not (oref this :file)))
624 (slot-boundp this :directory)
625 (oref this :directory))
626 (oset this :file (expand-file-name project-file-name (oref this :directory))))
627 (when (and (or (not (slot-boundp this :directory))
628 (not (oref this :directory)))
629 (slot-boundp this :file)
630 (oref this :file))
631 (oset this :directory (file-name-directory (oref this :file))))
632 )
633
634
635
636 \f
637 ;;; Hooks & Autoloads
638 ;;
639 ;; These let us watch various activities, and respond appropriately.
640
641 ;; (add-hook 'edebug-setup-hook
642 ;; (lambda ()
643 ;; (def-edebug-spec ede-with-projectfile
644 ;; (form def-body))))
645
646 (provide 'ede/base)
647
648 ;; Local variables:
649 ;; generated-autoload-file: "loaddefs.el"
650 ;; generated-autoload-load-name: "ede/base"
651 ;; End:
652
653 ;;; ede/base.el ends here