Mercurial > emacs
comparison lisp/cedet/ede/project-am.el @ 104496:8c4870c15962
* cedet/ede.el, cedet/ede/*.el: New files.
* cedet/cedet.el: Require ede.
* cedet/semantic/symref/filter.el (semantic-symref-hits-in-region):
Require semantic/idle.
author | Chong Yidong <cyd@stupidchicken.com> |
---|---|
date | Sun, 20 Sep 2009 15:06:05 +0000 |
parents | |
children | a6a812dd2d88 |
comparison
equal
deleted
inserted
replaced
104495:4659ddbe20bf | 104496:8c4870c15962 |
---|---|
1 ;;; project-am.el --- A project management scheme based on automake files. | |
2 | |
3 ;;; Copyright (C) 1998, 1999, 2000, 2003, 2005, 2007, 2008, 2009 | |
4 ;;; Free Software Foundation, Inc. | |
5 | |
6 ;; Author: Eric M. Ludlam <zappo@gnu.org> | |
7 ;; Version: 0.0.3 | |
8 ;; Keywords: project, make | |
9 | |
10 ;; This file is part of GNU Emacs. | |
11 | |
12 ;; GNU Emacs is free software: you can redistribute it and/or modify | |
13 ;; it under the terms of the GNU General Public License as published by | |
14 ;; the Free Software Foundation, either version 3 of the License, or | |
15 ;; (at your option) any later version. | |
16 | |
17 ;; GNU Emacs is distributed in the hope that it will be useful, | |
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 ;; GNU General Public License for more details. | |
21 | |
22 ;; You should have received a copy of the GNU General Public License | |
23 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
24 | |
25 ;;; Commentary: | |
26 ;; | |
27 ;; The GNU Automake tool is the first step towards having a really | |
28 ;; good project management system. It provides a simple and concise | |
29 ;; look at what is actually in a project, and records it in a simple | |
30 ;; fashion. | |
31 ;; | |
32 ;; project-am uses the structure defined in all good GNU projects with | |
33 ;; the Automake file as it's base template, and then maintains that | |
34 ;; information during edits, automatically updating the automake file | |
35 ;; where appropriate. | |
36 | |
37 | |
38 ;; (eval-and-compile | |
39 ;; ;; Compatibility for makefile mode. | |
40 ;; (condition-case nil | |
41 ;; (require 'makefile "make-mode") | |
42 ;; (error (require 'make-mode "make-mode"))) | |
43 | |
44 ;; ;; Requiring the .el files prevents incomplete builds. | |
45 ;; (require 'eieio "eieio.el") | |
46 ;; (require 'ede "ede.el")) | |
47 | |
48 (require 'make-mode) | |
49 (require 'ede) | |
50 (require 'ede/make) | |
51 (require 'ede/makefile-edit) | |
52 | |
53 (declare-function autoconf-parameters-for-macro "ede/autoconf-edit") | |
54 (eval-when-compile (require 'compile)) | |
55 | |
56 ;;; Code: | |
57 (defgroup project-am nil | |
58 "File and tag browser frame." | |
59 :group 'tools | |
60 :group 'ede | |
61 ) | |
62 | |
63 (defcustom project-am-compile-project-command nil | |
64 "*Default command used to compile a project." | |
65 :group 'project-am | |
66 :type 'string) | |
67 | |
68 (defcustom project-am-compile-target-command (concat ede-make-command " -k %s") | |
69 "*Default command used to compile a project." | |
70 :group 'project-am | |
71 :type 'string) | |
72 | |
73 (defcustom project-am-debug-target-function 'gdb | |
74 "*Default Emacs command used to debug a target." | |
75 :group 'project-am | |
76 :type 'sexp) ; make this be a list some day | |
77 | |
78 (defconst project-am-type-alist | |
79 '(("bin" project-am-program "bin_PROGRAMS" t) | |
80 ("sbin" project-am-program "sbin_PROGRAMS" t) | |
81 ("noinstbin" project-am-program "noinst_PROGRAMS" t) | |
82 ("checkbin" project-am-program "check_PROGRAMS" t) | |
83 ("lib" project-am-lib "lib_LIBS" t) | |
84 ("libraries" project-am-lib "lib_LIBRARIES" t) | |
85 ("librariesnoinst" project-am-lib "noinst_LIBRARIES" t) | |
86 ("pkglibraries" project-am-lib "pkglib_LIBRARIES" t) | |
87 ("checklibs" project-am-lib "check_LIBRARIES" t) | |
88 ("ltlibraries" project-am-lib "lib_LTLIBRARIES" t) | |
89 ("ltlibrariesnoinst" project-am-lib "noinst_LTLIBRARIES" t) | |
90 ("pkgltlibraries" project-am-lib "pkglib_LTLIBRARIES" t) | |
91 ("checkltlibs" project-am-lib "check_LTLIBRARIES" t) | |
92 ("headernoinst" project-am-header-noinst "noinst_HEADERS") | |
93 ("headerinst" project-am-header-inst "include_HEADERS") | |
94 ("headerpkg" project-am-header-pkg "pkginclude_HEADERS") | |
95 ("headerpkg" project-am-header-chk "check_HEADERS") | |
96 ("texinfo" project-am-texinfo "info_TEXINFOS" t) | |
97 ("man" project-am-man "man_MANS") | |
98 ("lisp" project-am-lisp "lisp_LISP") | |
99 ;; for other global files track EXTRA_ | |
100 ("extrabin" project-am-program "EXTRA_PROGRAMS" t) | |
101 ("builtsrcs" project-am-built-src "BUILT_SOURCES") | |
102 ("extradist" project-am-extra-dist "EXTRA_DIST") | |
103 ;; Custom libraries targets? | |
104 ;; ("ltlibcustom" project-am-lib ".*?_LTLIBRARIES" t) | |
105 ) | |
106 "Alist of type names and the type of object to create for them. | |
107 Each entry is of th form: | |
108 (EMACSNAME CLASS AUToMAKEVAR INDIRECT) | |
109 where EMACSNAME is a name for Emacs to use. | |
110 CLASS is the EDE target class to represent the target. | |
111 AUTOMAKEVAR is the Automake variable to identify. This cannot be a | |
112 regular expression. | |
113 INDIRECT is optional. If it is non-nil, then the variable in | |
114 question lists other variables that need to be looked up.") | |
115 | |
116 (defclass project-am-target (ede-target) | |
117 nil | |
118 "Base target class for everything in project-am.") | |
119 | |
120 (defclass project-am-objectcode (project-am-target) | |
121 ((source :initarg :source :documentation "List of source files.")) | |
122 "A target which creates object code, like a C program or library.") | |
123 | |
124 (defclass project-am-program (project-am-objectcode) | |
125 ((ldadd :initarg :ldadd :documentation "Additional LD args." | |
126 :initform nil)) | |
127 "A top level program to build") | |
128 | |
129 (defclass project-am-header (project-am-target) | |
130 () | |
131 "A group of misc source files, such as headers.") | |
132 | |
133 (defclass project-am-header-noinst (project-am-header) | |
134 () | |
135 "A group of header files that are not installed.") | |
136 | |
137 (defclass project-am-header-inst (project-am-header) | |
138 () | |
139 "A group of header files that are not installed.") | |
140 | |
141 (defclass project-am-header-pkg (project-am-header) | |
142 () | |
143 "A group of header files that are not installed.") | |
144 | |
145 (defclass project-am-header-chk (project-am-header) | |
146 () | |
147 "A group of header files that are not installed.") | |
148 | |
149 (defclass project-am-lib (project-am-objectcode) | |
150 nil | |
151 "A top level library to build") | |
152 | |
153 (defclass project-am-lisp (project-am-target) | |
154 () | |
155 "A group of Emacs Lisp programs to byte compile.") | |
156 | |
157 (defclass project-am-texinfo (project-am-target) | |
158 ((include :initarg :include | |
159 :initform nil | |
160 :documentation "Additional texinfo included in this one.")) | |
161 "A top level texinfo file to build.") | |
162 | |
163 (defclass project-am-man (project-am-target) | |
164 nil | |
165 "A top level man file to build.") | |
166 | |
167 ;; For generic files tracker like EXTRA_DIST | |
168 (defclass project-am-built-src (project-am-target) | |
169 () | |
170 "A group of Emacs Lisp programs to byte compile.") | |
171 | |
172 (defclass project-am-extra-dist (project-am-target) | |
173 () | |
174 "A group of Emacs Lisp programs to byte compile.") | |
175 | |
176 (defclass project-am-makefile (ede-project) | |
177 ((targets :initarg :targets | |
178 :initform nil | |
179 :documentation "Top level targets in this makefile.") | |
180 (configureoutputfiles | |
181 :initform nil | |
182 :documentation | |
183 "List of files output from configure system.") | |
184 ) | |
185 "Encode one makefile.") | |
186 | |
187 ;;; Code: | |
188 (defmethod project-add-file ((ot project-am-target)) | |
189 "Add the current buffer into a project. | |
190 OT is the object target. DIR is the directory to start in." | |
191 (let* ((target (if ede-object (error "Already assocated w/ a target") | |
192 (let ((amf (project-am-load default-directory))) | |
193 (if (not amf) (error "No project file")) | |
194 (completing-read "Target: " | |
195 (object-assoc-list 'name | |
196 (oref amf targets)) | |
197 nil t)))) | |
198 ;; The input target might be new. See if we can find it. | |
199 (amf (ede-load-project-file (oref ot path))) | |
200 (ot (object-assoc target 'name (oref amf targets))) | |
201 (ofn (file-name-nondirectory (buffer-file-name)))) | |
202 (if (not ot) | |
203 (setq ot | |
204 (project-new-target | |
205 target (project-am-preferred-target-type (buffer-file-name))))) | |
206 (ede-with-projectfile ot | |
207 (makefile-move-to-macro (project-am-macro ot)) | |
208 (ede-maybe-checkout) | |
209 (makefile-end-of-command) | |
210 (insert " " ofn) | |
211 (makefile-fill-paragraph nil) | |
212 (project-rescan ot) | |
213 (save-buffer)) | |
214 (setq ede-object ot))) | |
215 | |
216 (defmethod project-remove-file ((ot project-am-target) fnnd) | |
217 "Remove the current buffer from any project targets." | |
218 (ede-with-projectfile ot | |
219 (makefile-move-to-macro (project-am-macro ot)) | |
220 (if (and buffer-read-only vc-mode | |
221 (y-or-n-p "Checkout Makefile.am from VC? ")) | |
222 (vc-toggle-read-only t)) | |
223 (ede-maybe-checkout) | |
224 (makefile-navigate-macro (concat " *" (regexp-quote (ede-name fnnd)))) | |
225 (replace-match "" t t nil 0) | |
226 (makefile-fill-paragraph nil) | |
227 (project-rescan ot) | |
228 (save-buffer)) | |
229 (setq ede-object nil)) | |
230 | |
231 (defmethod project-edit-file-target ((obj project-am-target)) | |
232 "Edit the target associated w/ this file." | |
233 (find-file (concat (oref obj path) "Makefile.am")) | |
234 (goto-char (point-min)) | |
235 (makefile-move-to-macro (project-am-macro obj)) | |
236 (if (= (point-min) (point)) | |
237 (re-search-forward (ede-target-name obj)))) | |
238 | |
239 (defmethod project-new-target ((proj project-am-makefile) | |
240 &optional name type) | |
241 "Create a new target named NAME. | |
242 Argument TYPE is the type of target to insert. This is a string | |
243 matching something in `project-am-type-alist' or type class symbol. | |
244 Despite the fact that this is a method, it depends on the current | |
245 buffer being in order to provide a smart default target type." | |
246 (let* ((name (or name (read-string "Name: " ""))) | |
247 (type (or type | |
248 (completing-read "Type: " | |
249 project-am-type-alist | |
250 nil t | |
251 (cond ((eq major-mode 'texinfo-mode) | |
252 "texinfo") | |
253 ((eq major-mode 'nroff-mode) | |
254 "man") | |
255 ((eq major-mode 'emacs-lisp-mode) | |
256 "lisp") | |
257 (t "bin"))))) | |
258 (ntype (assoc type project-am-type-alist)) | |
259 (ot nil)) | |
260 (setq ot (apply (car (cdr ntype)) name :name name | |
261 :path (expand-file-name default-directory) nil)) | |
262 (if (not ot) (error "Error creating target object %S" ntype)) | |
263 (ede-with-projectfile ot | |
264 (goto-char (point-min)) | |
265 (ede-maybe-checkout) | |
266 (makefile-next-dependency) | |
267 (if (= (point) (point-min)) | |
268 (goto-char (point-max)) | |
269 (beginning-of-line) | |
270 (insert "\n") | |
271 (forward-char -1)) | |
272 ;; Add the new target sources macro (if needed) | |
273 (if (project-am-macro ot) | |
274 (makefile-insert-macro (project-am-macro ot))) | |
275 ;; Add to the list of objects. | |
276 (goto-char (point-min)) | |
277 (makefile-move-to-macro (car (cdr (cdr ntype)))) | |
278 (if (= (point) (point-min)) | |
279 (progn | |
280 (if (re-search-forward makefile-macroassign-regex nil t) | |
281 (progn (forward-line -1) | |
282 (end-of-line) | |
283 (insert "\n")) | |
284 ;; If the above search fails, thats ok. We'd just want to be at | |
285 ;; point-min anyway. | |
286 ) | |
287 (makefile-insert-macro (car (cdr (cdr ntype)))))) | |
288 (makefile-end-of-command) | |
289 (insert " " (ede-target-name ot)) | |
290 (save-buffer) | |
291 ;; Rescan the object in this makefile. | |
292 (project-rescan ede-object)))) | |
293 | |
294 ;(defun project-am-rescan-toplevel () | |
295 ; "Rescan all projects in which the current buffer resides." | |
296 ; (interactive) | |
297 ; (let* ((tlof (project-am-find-topmost-level default-directory)) | |
298 ; (tlo (project-am-load tlof)) | |
299 ; (ede-deep-rescan t)) ; scan deep in this case. | |
300 ; ;; tlo is the top level object for whatever file we are in | |
301 ; ;; or nil. If we have an object, call the rescan method. | |
302 ; (if tlo (project-am-rescan tlo)))) | |
303 | |
304 ;; | |
305 ;; NOTE TO SELF | |
306 ;; | |
307 ;; This should be handled at the EDE level, calling a method of the | |
308 ;; top most project. | |
309 ;; | |
310 (defmethod project-compile-project ((obj project-am-target) &optional command) | |
311 "Compile the entire current project. | |
312 Argument COMMAND is the command to use when compiling." | |
313 (require 'compile) | |
314 (if (not command) | |
315 (setq | |
316 command | |
317 ;; This interactive statement was taken from compile, and I'll | |
318 ;; use the same command history too. | |
319 (progn | |
320 (if (not project-am-compile-project-command) | |
321 (setq project-am-compile-project-command compile-command)) | |
322 (if (or compilation-read-command current-prefix-arg) | |
323 (read-from-minibuffer "Project compile command: " | |
324 ;; hardcode make -k | |
325 ;; This is compile project after all. | |
326 project-am-compile-project-command | |
327 nil nil '(compile-history . 1)) | |
328 project-am-compile-project-command)))) | |
329 ;; When compile a project, we might be in a subdirectory, | |
330 ;; so we have to make sure we move all the way to the top. | |
331 (let* ((default-directory (project-am-find-topmost-level default-directory))) | |
332 (compile command))) | |
333 | |
334 (defmethod project-compile-project ((obj project-am-makefile) | |
335 &optional command) | |
336 "Compile the entire current project. | |
337 Argument COMMAND is the command to use when compiling." | |
338 (require 'compile) | |
339 (if (not command) | |
340 (setq | |
341 command | |
342 ;; This interactive statement was taken from compile, and I'll | |
343 ;; use the same command history too. | |
344 (progn | |
345 (if (not project-am-compile-project-command) | |
346 (setq project-am-compile-project-command compile-command)) | |
347 (if (or compilation-read-command current-prefix-arg) | |
348 (read-from-minibuffer "Project compile command: " | |
349 ;; hardcode make -k | |
350 ;; This is compile project after all. | |
351 project-am-compile-project-command | |
352 nil nil '(compile-history . 1)) | |
353 project-am-compile-project-command)))) | |
354 ;; When compile a project, we might be in a subdirectory, | |
355 ;; so we have to make sure we move all the way to the top. | |
356 (let* ((default-directory (project-am-find-topmost-level default-directory))) | |
357 (compile command))) | |
358 | |
359 (defmethod project-compile-target ((obj project-am-target) &optional command) | |
360 "Compile the current target. | |
361 Argument COMMAND is the command to use for compiling the target." | |
362 (require 'compile) | |
363 (if (not project-am-compile-project-command) | |
364 (setq project-am-compile-project-command compile-command)) | |
365 (if (not command) | |
366 (setq | |
367 command | |
368 (if compilation-read-command | |
369 (read-from-minibuffer "Project compile command: " | |
370 ;; hardcode make -k | |
371 ;; This is compile project after all. | |
372 (if ede-object | |
373 (format | |
374 project-am-compile-target-command | |
375 (project-compile-target-command | |
376 ede-object)) | |
377 project-am-compile-target-command) | |
378 nil nil | |
379 '(compile-history . 1)) | |
380 (if ede-object | |
381 project-am-compile-project-command | |
382 (format | |
383 project-am-compile-target-command | |
384 (project-compile-target-command ede-object)))))) | |
385 ;; We better be in the right place when compiling a specific target. | |
386 (compile command)) | |
387 | |
388 (defmethod project-debug-target ((obj project-am-objectcode)) | |
389 "Run the current project target in a debugger." | |
390 (let ((tb (get-buffer-create " *padt*")) | |
391 (dd (oref obj path)) | |
392 (cmd nil)) | |
393 (unwind-protect | |
394 (progn | |
395 (set-buffer tb) | |
396 (setq default-directory dd) | |
397 (setq cmd (read-from-minibuffer | |
398 "Run (like this): " | |
399 (concat (symbol-name project-am-debug-target-function) | |
400 " " (ede-target-name obj)))) | |
401 (funcall project-am-debug-target-function cmd)) | |
402 (kill-buffer tb)))) | |
403 | |
404 (defmethod project-make-dist ((this project-am-target)) | |
405 "Run the current project in the debugger." | |
406 (require 'compile) | |
407 (if (not project-am-compile-project-command) | |
408 (setq project-am-compile-project-command compile-command)) | |
409 (project-compile-project this (concat project-am-compile-project-command | |
410 " dist"))) | |
411 | |
412 ;;; Project loading and saving | |
413 ;; | |
414 (defun project-am-load (project &optional rootproj) | |
415 "Read an automakefile PROJECT into our data structure. | |
416 Make sure that the tree down to our makefile is complete so that there | |
417 is cohesion in the project. Return the project file (or sub-project). | |
418 If a given set of projects has already been loaded, then do nothing | |
419 but return the project for the directory given. | |
420 Optional ROOTPROJ is the root EDE project." | |
421 ;; @TODO - rationalize this to the newer EDE way of doing things. | |
422 (setq project (expand-file-name project)) | |
423 (let* ((ede-constructing t) | |
424 (fn (project-am-find-topmost-level (file-name-as-directory project))) | |
425 (amo nil) | |
426 (trimmed (if (string-match (regexp-quote fn) | |
427 project) | |
428 (replace-match "" t t project) | |
429 "")) | |
430 (subdir nil)) | |
431 (setq amo (object-assoc (expand-file-name "Makefile.am" fn) | |
432 'file ede-projects)) | |
433 (if amo | |
434 (error "Synchronous error in ede/project-am objects") | |
435 (let ((project-am-constructing t)) | |
436 (setq amo (project-am-load-makefile fn)))) | |
437 (if (not amo) | |
438 nil | |
439 ;; Now scan down from amo, and find the current directory | |
440 ;; from the PROJECT file. | |
441 (while (< 0 (length trimmed)) | |
442 (if (string-match "\\([a-zA-Z0-9.-]+\\)/" trimmed) | |
443 (setq subdir (match-string 0 trimmed) | |
444 trimmed (replace-match "" t t trimmed)) | |
445 (error "Error scanning down path for project")) | |
446 (setq amo (project-am-subtree | |
447 amo | |
448 (expand-file-name "Makefile.am" | |
449 (expand-file-name subdir fn))) | |
450 fn (expand-file-name subdir fn))) | |
451 amo) | |
452 )) | |
453 | |
454 (defun project-am-find-topmost-level (dir) | |
455 "Find the topmost automakefile starting with DIR." | |
456 (let ((newdir dir)) | |
457 (while (or (file-exists-p (concat newdir "Makefile.am")) | |
458 (file-exists-p (concat newdir "configure.ac")) | |
459 (file-exists-p (concat newdir "configure.in")) | |
460 ) | |
461 (setq dir newdir newdir | |
462 (file-name-directory (directory-file-name newdir)))) | |
463 (expand-file-name dir))) | |
464 | |
465 (defmacro project-am-with-makefile-current (dir &rest forms) | |
466 "Set the Makefile.am in DIR to be the current buffer. | |
467 Run FORMS while the makefile is current. | |
468 Kill the makefile if it was not loaded before the load." | |
469 `(let* ((fn (expand-file-name "Makefile.am" ,dir)) | |
470 (fb nil) | |
471 (kb (get-file-buffer fn))) | |
472 (if (not (file-exists-p fn)) | |
473 nil | |
474 (save-excursion | |
475 (if kb (setq fb kb) | |
476 ;; We need to find-file this thing, but don't use | |
477 ;; any semantic features. | |
478 (let ((semantic-init-hooks nil)) | |
479 (setq fb (find-file-noselect fn))) | |
480 ) | |
481 (set-buffer fb) | |
482 (prog1 ,@forms | |
483 (if (not kb) (kill-buffer (current-buffer)))))))) | |
484 (put 'project-am-with-makefile-current 'lisp-indent-function 1) | |
485 | |
486 (add-hook 'edebug-setup-hook | |
487 (lambda () | |
488 (def-edebug-spec project-am-with-makefile-current | |
489 (form def-body)))) | |
490 | |
491 | |
492 (defun project-am-load-makefile (path) | |
493 "Convert PATH into a project Makefile, and return its project object. | |
494 It does not check for existing project objects. Use `project-am-load'." | |
495 (project-am-with-makefile-current path | |
496 (if (and ede-object (project-am-makefile-p ede-object)) | |
497 ede-object | |
498 (let* ((pi (project-am-package-info path)) | |
499 (pn (or (nth 0 pi) (project-am-last-dir fn))) | |
500 (ver (or (nth 1 pi) "0.0")) | |
501 (bug (nth 2 pi)) | |
502 (cof (nth 3 pi)) | |
503 (ampf (project-am-makefile | |
504 pn :name pn | |
505 :version ver | |
506 :mailinglist (or bug "") | |
507 :file fn))) | |
508 (oset ampf :directory (file-name-directory fn)) | |
509 (oset ampf configureoutputfiles cof) | |
510 (make-local-variable 'ede-object) | |
511 (setq ede-object ampf) | |
512 ;; Move the rescan after we set ede-object to prevent recursion | |
513 (project-rescan ampf) | |
514 ampf)))) | |
515 | |
516 ;;; Methods: | |
517 (defmethod ede-find-target ((amf project-am-makefile) buffer) | |
518 "Fetch the target belonging to BUFFER." | |
519 (or (call-next-method) | |
520 (let ((targ (oref amf targets)) | |
521 (sobj (oref amf subproj)) | |
522 (obj nil)) | |
523 (while (and targ (not obj)) | |
524 (if (ede-buffer-mine (car targ) buffer) | |
525 (setq obj (car targ))) | |
526 (setq targ (cdr targ))) | |
527 (while (and sobj (not obj)) | |
528 (setq obj (project-am-buffer-object (car sobj) buffer) | |
529 sobj (cdr sobj))) | |
530 obj))) | |
531 | |
532 (defmethod project-targets-for-file ((proj project-am-makefile)) | |
533 "Return a list of targets the project PROJ." | |
534 (oref proj targets)) | |
535 | |
536 (defun project-am-scan-for-targets (currproj dir) | |
537 "Scan the current Makefile.am for targets. | |
538 CURRPROJ is the current project being scanned. | |
539 DIR is the directory to apply to new targets." | |
540 (let* ((otargets (oref currproj targets)) | |
541 (ntargets nil) | |
542 (tmp nil) | |
543 ) | |
544 (mapc | |
545 ;; Map all the different types | |
546 (lambda (typecar) | |
547 (let ((macro (nth 2 typecar)) | |
548 (class (nth 1 typecar)) | |
549 (indirect (nth 3 typecar)) | |
550 ;(name (car typecar)) | |
551 ) | |
552 (if indirect | |
553 ;; Map all the found objects | |
554 (mapc (lambda (lstcar) | |
555 (setq tmp (object-assoc lstcar 'name otargets)) | |
556 (when (not tmp) | |
557 (setq tmp (apply class lstcar :name lstcar | |
558 :path dir nil))) | |
559 (project-rescan tmp) | |
560 (setq ntargets (cons tmp ntargets))) | |
561 (makefile-macro-file-list macro)) | |
562 ;; Non-indirect will have a target whos sources | |
563 ;; are actual files, not names of other targets. | |
564 (let ((files (makefile-macro-file-list macro))) | |
565 (when files | |
566 (setq tmp (object-assoc macro 'name otargets)) | |
567 (when (not tmp) | |
568 (setq tmp (apply class macro :name macro | |
569 :path dir nil))) | |
570 (project-rescan tmp) | |
571 (setq ntargets (cons tmp ntargets)) | |
572 )) | |
573 ) | |
574 )) | |
575 project-am-type-alist) | |
576 ntargets)) | |
577 | |
578 (defmethod project-rescan ((this project-am-makefile)) | |
579 "Rescan the makefile for all targets and sub targets." | |
580 (project-am-with-makefile-current (file-name-directory (oref this file)) | |
581 ;;(message "Scanning %s..." (oref this file)) | |
582 (let* ((pi (project-am-package-info (oref this directory))) | |
583 (pn (nth 0 pi)) | |
584 (pv (nth 1 pi)) | |
585 (bug (nth 2 pi)) | |
586 (cof (nth 3 pi)) | |
587 (osubproj (oref this subproj)) | |
588 (csubproj (or | |
589 ;; If DIST_SUBDIRS doesn't exist, then go for the | |
590 ;; static list of SUBDIRS. The DIST version should | |
591 ;; contain SUBDIRS plus extra stuff. | |
592 (makefile-macro-file-list "DIST_SUBDIRS") | |
593 (makefile-macro-file-list "SUBDIRS"))) | |
594 (csubprojexpanded nil) | |
595 (nsubproj nil) | |
596 ;; Targets are excluded here because they require | |
597 ;; special attention. | |
598 (dir (expand-file-name default-directory)) | |
599 (tmp nil) | |
600 (ntargets (project-am-scan-for-targets this dir)) | |
601 ) | |
602 | |
603 (and pn (string= (directory-file-name | |
604 (oref this directory)) | |
605 (directory-file-name | |
606 (project-am-find-topmost-level | |
607 (oref this directory)))) | |
608 (oset this name pn) | |
609 (and pv (oset this version pv)) | |
610 (and bug (oset this mailinglist bug)) | |
611 (oset this configureoutputfiles cof)) | |
612 | |
613 ; ;; LISP is different. Here there is only one kind of lisp (that I know of | |
614 ; ;; anyway) so it doesn't get mapped when it is found. | |
615 ; (if (makefile-move-to-macro "lisp_LISP") | |
616 ; (let ((tmp (project-am-lisp "lisp" | |
617 ; :name "lisp" | |
618 ; :path dir))) | |
619 ; (project-rescan tmp) | |
620 ; (setq ntargets (cons tmp ntargets)))) | |
621 ; | |
622 ;; Now that we have this new list, chuck the old targets | |
623 ;; and replace it with the new list of targets I just created. | |
624 (oset this targets (nreverse ntargets)) | |
625 ;; We still have a list of targets. For all buffers, make sure | |
626 ;; their object still exists! | |
627 | |
628 ;; FIGURE THIS OUT | |
629 | |
630 (mapc (lambda (sp) | |
631 (let ((var (makefile-extract-varname-from-text sp)) | |
632 ) | |
633 (if (not var) | |
634 (setq csubprojexpanded (cons sp csubprojexpanded)) | |
635 ;; If it is a variable, expand that variable, and keep going. | |
636 (let ((varexp (makefile-macro-file-list var))) | |
637 (dolist (V varexp) | |
638 (setq csubprojexpanded (cons V csubprojexpanded))))) | |
639 )) | |
640 csubproj) | |
641 | |
642 ;; Ok, now lets look at all our sub-projects. | |
643 (mapc (lambda (sp) | |
644 (let* ((subdir (file-name-as-directory | |
645 (expand-file-name | |
646 sp (file-name-directory (oref this :file))))) | |
647 (submake (expand-file-name | |
648 "Makefile.am" | |
649 subdir))) | |
650 (if (string= submake (oref this :file)) | |
651 nil ;; don't recurse.. please! | |
652 | |
653 ;; For each project id found, see if we need to recycle, | |
654 ;; and if we do not, then make a new one. Check the deep | |
655 ;; rescan value for behavior patterns. | |
656 (setq tmp (object-assoc | |
657 submake | |
658 'file osubproj)) | |
659 (if (not tmp) | |
660 (setq tmp | |
661 (condition-case nil | |
662 ;; In case of problem, ignore it. | |
663 (project-am-load-makefile subdir) | |
664 (error nil))) | |
665 ;; If we have tmp, then rescan it only if deep mode. | |
666 (if ede-deep-rescan | |
667 (project-rescan tmp))) | |
668 ;; Tac tmp onto our list of things to keep, but only | |
669 ;; if tmp was found. | |
670 (when tmp | |
671 ;;(message "Adding %S" (object-print tmp)) | |
672 (setq nsubproj (cons tmp nsubproj))))) | |
673 ) | |
674 (nreverse csubprojexpanded)) | |
675 (oset this subproj nsubproj) | |
676 ;; All elements should be updated now. | |
677 ))) | |
678 | |
679 | |
680 (defmethod project-rescan ((this project-am-program)) | |
681 "Rescan object THIS." | |
682 (oset this :source (makefile-macro-file-list (project-am-macro this))) | |
683 (oset this :ldadd (makefile-macro-file-list | |
684 (concat (oref this :name) "_LDADD")))) | |
685 | |
686 (defmethod project-rescan ((this project-am-lib)) | |
687 "Rescan object THIS." | |
688 (oset this :source (makefile-macro-file-list (project-am-macro this)))) | |
689 | |
690 (defmethod project-rescan ((this project-am-texinfo)) | |
691 "Rescan object THIS." | |
692 (oset this :include (makefile-macro-file-list (project-am-macro this)))) | |
693 | |
694 (defmethod project-rescan ((this project-am-man)) | |
695 "Rescan object THIS." | |
696 (oset this :source (makefile-macro-file-list (project-am-macro this)))) | |
697 | |
698 (defmethod project-rescan ((this project-am-lisp)) | |
699 "Rescan the lisp sources." | |
700 (oset this :source (makefile-macro-file-list (project-am-macro this)))) | |
701 | |
702 (defmethod project-rescan ((this project-am-header)) | |
703 "Rescan the Header sources for object THIS." | |
704 (oset this :source (makefile-macro-file-list (project-am-macro this)))) | |
705 | |
706 (defmethod project-rescan ((this project-am-built-src)) | |
707 "Rescan built sources for object THIS." | |
708 (oset this :source (makefile-macro-file-list "BUILT_SOURCES"))) | |
709 | |
710 (defmethod project-rescan ((this project-am-extra-dist)) | |
711 "Rescan object THIS." | |
712 (oset this :source (makefile-macro-file-list "EXTRA_DIST"))) | |
713 ;; NOTE: The below calls 'file' then checks that it is some sort of | |
714 ;; text file. The file command may not be available on all platforms | |
715 ;; and some files may not exist yet. (ie - auto-generated) | |
716 | |
717 ;;(mapc | |
718 ;; (lambda (f) | |
719 ;; ;; prevent garbage to be parsed, could we use :aux ? | |
720 ;; (if (and (not (member f (oref this :source))) | |
721 ;; (string-match-p "ASCII\\|text" | |
722 ;; (shell-command-to-string | |
723 ;; (concat "file " f)))) | |
724 ;; (oset this :source (cons f (oref this :source))))) | |
725 ;; (makefile-macro-file-list "EXTRA_DIST"))) | |
726 | |
727 (defmethod project-am-macro ((this project-am-objectcode)) | |
728 "Return the default macro to 'edit' for this object type." | |
729 (concat (subst-char-in-string ?- ?_ (oref this :name)) "_SOURCES")) | |
730 | |
731 (defmethod project-am-macro ((this project-am-header-noinst)) | |
732 "Return the default macro to 'edit' for this object." | |
733 "noinst_HEADERS") | |
734 | |
735 (defmethod project-am-macro ((this project-am-header-inst)) | |
736 "Return the default macro to 'edit' for this object." | |
737 "include_HEADERS") | |
738 | |
739 (defmethod project-am-macro ((this project-am-header-pkg)) | |
740 "Return the default macro to 'edit' for this object." | |
741 "pkginclude_HEADERS") | |
742 | |
743 (defmethod project-am-macro ((this project-am-header-chk)) | |
744 "Return the default macro to 'edit' for this object." | |
745 "check_HEADERS") | |
746 | |
747 (defmethod project-am-macro ((this project-am-texinfo)) | |
748 "Return the default macro to 'edit' for this object type." | |
749 (concat (file-name-sans-extension (oref this :name)) "_TEXINFOS")) | |
750 | |
751 (defmethod project-am-macro ((this project-am-man)) | |
752 "Return the default macro to 'edit' for this object type." | |
753 (oref this :name)) | |
754 | |
755 (defmethod project-am-macro ((this project-am-lisp)) | |
756 "Return the default macro to 'edit' for this object." | |
757 "lisp_LISP") | |
758 | |
759 (defun project-am-buffer-object (amf buffer) | |
760 "Return an object starting with AMF associated with BUFFER. | |
761 nil means that this buffer belongs to no-one." | |
762 (if (not amf) | |
763 nil | |
764 (if (ede-buffer-mine amf buffer) | |
765 amf | |
766 (let ((targ (oref amf targets)) | |
767 (sobj (oref amf subproj)) | |
768 (obj nil)) | |
769 (while (and targ (not obj)) | |
770 (if (ede-buffer-mine (car targ) buffer) | |
771 (setq obj (car targ))) | |
772 (setq targ (cdr targ))) | |
773 (while (and sobj (not obj)) | |
774 (setq obj (project-am-buffer-object (car sobj) buffer) | |
775 sobj (cdr sobj))) | |
776 obj)))) | |
777 | |
778 (defmethod ede-buffer-mine ((this project-am-makefile) buffer) | |
779 "Return t if object THIS lays claim to the file in BUFFER." | |
780 (let ((efn (expand-file-name (buffer-file-name buffer)))) | |
781 (or (string= (oref this :file) efn) | |
782 (string-match "/configure\\.ac$" efn) | |
783 (string-match "/configure\\.in$" efn) | |
784 (string-match "/configure$" efn) | |
785 ;; Search output files. | |
786 (let ((ans nil)) | |
787 (dolist (f (oref this configureoutputfiles)) | |
788 (when (string-match (concat (regexp-quote f) "$") efn) | |
789 (setq ans t))) | |
790 ans) | |
791 ))) | |
792 | |
793 (defmethod ede-buffer-mine ((this project-am-objectcode) buffer) | |
794 "Return t if object THIS lays claim to the file in BUFFER." | |
795 (member (file-name-nondirectory (buffer-file-name buffer)) | |
796 (oref this :source))) | |
797 | |
798 (defmethod ede-buffer-mine ((this project-am-texinfo) buffer) | |
799 "Return t if object THIS lays claim to the file in BUFFER." | |
800 (let ((bfn (buffer-file-name buffer))) | |
801 (or (string= (oref this :name) (file-name-nondirectory bfn)) | |
802 (member (file-name-nondirectory bfn) (oref this :include))))) | |
803 | |
804 (defmethod ede-buffer-mine ((this project-am-man) buffer) | |
805 "Return t if object THIS lays claim to the file in BUFFER." | |
806 (string= (oref this :name) (buffer-file-name buffer))) | |
807 | |
808 (defmethod ede-buffer-mine ((this project-am-lisp) buffer) | |
809 "Return t if object THIS lays claim to the file in BUFFER." | |
810 (member (file-name-nondirectory (buffer-file-name buffer)) | |
811 (oref this :source))) | |
812 | |
813 (defmethod project-am-subtree ((ampf project-am-makefile) subdir) | |
814 "Return the sub project in AMPF specified by SUBDIR." | |
815 (object-assoc (expand-file-name subdir) 'file (oref ampf subproj))) | |
816 | |
817 (defmethod project-compile-target-command ((this project-am-target)) | |
818 "Default target to use when compiling a given target." | |
819 ;; This is a pretty good default for most. | |
820 "") | |
821 | |
822 (defmethod project-compile-target-command ((this project-am-objectcode)) | |
823 "Default target to use when compiling an object code target." | |
824 (oref this :name)) | |
825 | |
826 (defmethod project-compile-target-command ((this project-am-texinfo)) | |
827 "Default target t- use when compling a texinfo file." | |
828 (let ((n (oref this :name))) | |
829 (if (string-match "\\.texi?\\(nfo\\)?" n) | |
830 (setq n (replace-match ".info" t t n))) | |
831 n)) | |
832 | |
833 | |
834 ;;; Generic useful functions | |
835 | |
836 (defun project-am-last-dir (file) | |
837 "Return the last part of a directory name. | |
838 Argument FILE is the file to extract the end directory name from." | |
839 (let* ((s (file-name-directory file)) | |
840 (d (directory-file-name s)) | |
841 ) | |
842 (file-name-nondirectory d)) | |
843 ) | |
844 | |
845 (defun project-am-preferred-target-type (file) | |
846 "For FILE, return the preferred type for that file." | |
847 (cond ((string-match "\\.texi?\\(nfo\\)$" file) | |
848 project-am-texinfo) | |
849 ((string-match "\\.[0-9]$" file) | |
850 project-am-man) | |
851 ((string-match "\\.el$" file) | |
852 project-am-lisp) | |
853 (t | |
854 project-am-program))) | |
855 | |
856 (defmethod ede-buffer-header-file((this project-am-objectcode) buffer) | |
857 "There are no default header files." | |
858 (or (call-next-method) | |
859 (let ((s (oref this source)) | |
860 (found nil)) | |
861 (while (and s (not found)) | |
862 ;; Add more logic here if applicable. | |
863 (if (string-match "\\.\\(h\\|H\\|hh\\|hpp\\)" (car s)) | |
864 (setq found (car s))) | |
865 (setq s (cdr s))) | |
866 found))) | |
867 | |
868 (defmethod ede-documentation ((this project-am-texinfo)) | |
869 "Return a list of files that provides documentation. | |
870 Documentation is not for object THIS, but is provided by THIS for other | |
871 files in the project." | |
872 (let* ((src (append (oref this source) | |
873 (oref this include))) | |
874 (proj (ede-target-parent this)) | |
875 (dir (oref proj directory)) | |
876 (out nil)) | |
877 ;; Loop over all entries and expand | |
878 (while src | |
879 (setq out (cons | |
880 (expand-file-name (car src) dir) | |
881 out)) | |
882 (setq src (cdr src))) | |
883 ;; return it | |
884 out)) | |
885 | |
886 | |
887 ;;; Configure.in queries. | |
888 ;; | |
889 (defvar project-am-autoconf-file-options | |
890 '("configure.in" "configure.ac") | |
891 "List of possible configure files to look in for project info.") | |
892 | |
893 (defun project-am-autoconf-file (dir) | |
894 "Return the name of the autoconf file to use in DIR." | |
895 (let ((ans nil)) | |
896 (dolist (L project-am-autoconf-file-options) | |
897 (when (file-exists-p (expand-file-name L dir)) | |
898 (setq ans (expand-file-name L dir)))) | |
899 ans)) | |
900 | |
901 (defmacro project-am-with-config-current (file &rest forms) | |
902 "Set the Configure FILE in the top most directory above DIR as current. | |
903 Run FORMS in the configure file. | |
904 Kill the Configure buffer if it was not already in a buffer." | |
905 `(save-excursion | |
906 (let ((fb (generate-new-buffer ,file))) | |
907 (set-buffer fb) | |
908 (erase-buffer) | |
909 (insert-file-contents ,file) | |
910 (prog1 ,@forms | |
911 (kill-buffer fb))))) | |
912 | |
913 (put 'project-am-with-config-current 'lisp-indent-function 1) | |
914 | |
915 (add-hook 'edebug-setup-hook | |
916 (lambda () | |
917 (def-edebug-spec project-am-with-config-current | |
918 (form def-body)))) | |
919 | |
920 (defmacro project-am-extract-shell-variable (var) | |
921 "Extract the value of the shell variable VAR from a shell script." | |
922 (save-excursion | |
923 (goto-char (point-min)) | |
924 (when (re-search-forward (concat "^" (regexp-quote var) "\\s-*=\\s-*") | |
925 nil t) | |
926 (buffer-substring-no-properties (point) (point-at-eol))))) | |
927 | |
928 (defun project-am-extract-package-info (dir) | |
929 "Extract the package information for directory DIR." | |
930 (let ((conf-in (project-am-autoconf-file dir)) | |
931 (conf-sh (expand-file-name "configure" dir)) | |
932 (name (file-name-nondirectory | |
933 (directory-file-name dir))) | |
934 (ver "1.0") | |
935 (bugrep nil) | |
936 (configfiles nil) | |
937 ) | |
938 (cond | |
939 ;; Try configure.in or configure.ac | |
940 (conf-in | |
941 (require 'ede/autoconf-edit) | |
942 (project-am-with-config-current conf-in | |
943 (let ((aci (autoconf-parameters-for-macro "AC_INIT")) | |
944 (aia (autoconf-parameters-for-macro "AM_INIT_AUTOMAKE")) | |
945 (acf (autoconf-parameters-for-macro "AC_CONFIG_FILES")) | |
946 (aco (autoconf-parameters-for-macro "AC_OUTPUT")) | |
947 ) | |
948 (cond | |
949 ;; AC init has more than 1 parameter | |
950 ((> (length aci) 1) | |
951 (setq name (nth 0 aci) | |
952 ver (nth 1 aci) | |
953 bugrep (nth 2 aci))) | |
954 ;; The init automake has more than 1 parameter | |
955 ((> (length aia) 1) | |
956 (setq name (nth 0 aia) | |
957 ver (nth 1 aia) | |
958 bugrep (nth 2 aia))) | |
959 ) | |
960 ;; AC_CONFIG_FILES, or AC_OUTPUT lists everything that | |
961 ;; should be detected as part of this PROJECT, but not in a | |
962 ;; particular TARGET. | |
963 (let ((outfiles (cond (aco (list (car aco))) | |
964 (t acf)))) | |
965 (if (> (length outfiles) 1) | |
966 (setq configfiles outfiles) | |
967 (setq configfiles (split-string (car outfiles) " " t))) | |
968 ) | |
969 )) | |
970 ) | |
971 ;; Else, try the script | |
972 ((file-exists-p conf-sh) | |
973 (project-am-with-config-current conf-sh | |
974 (setq name (project-am-extract-shell-variable "PACKAGE_NAME") | |
975 ver (project-am-extract-shell-variable "PACKAGE_VERSION") | |
976 ) | |
977 )) | |
978 ;; Don't know what else.... | |
979 (t | |
980 nil)) | |
981 ;; Return stuff | |
982 (list name ver bugrep configfiles) | |
983 )) | |
984 | |
985 (defun project-am-package-info (dir) | |
986 "Get the package information for directory topmost project dir over DIR. | |
987 Calcultes the info with `project-am-extract-package-info'." | |
988 (let ((top (ede-toplevel))) | |
989 (when top (setq dir (oref top :directory))) | |
990 (project-am-extract-package-info dir))) | |
991 | |
992 (provide 'ede/project-am) | |
993 | |
994 ;;; ede/project-am.el ends here |