]> code.delx.au - gnu-emacs/blob - lisp/cedet/ede/project-am.el
Merge changes from emacs-23 branch
[gnu-emacs] / lisp / cedet / ede / project-am.el
1 ;;; project-am.el --- A project management scheme based on automake files.
2
3 ;; Copyright (C) 1998, 1999, 2000, 2003, 2005, 2007, 2008, 2009, 2010
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 associated 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 (require 'ede/shell)
396 (set-buffer tb)
397 (setq default-directory dd)
398 (setq cmd (read-from-minibuffer
399 "Run (like this): "
400 (concat (symbol-name project-am-debug-target-function)
401 " " (ede-target-name obj))))
402 (funcall project-am-debug-target-function cmd))
403 (kill-buffer tb))))
404
405 (declare-function ede-shell-run-something "ede/shell")
406
407 (defmethod project-run-target ((obj project-am-objectcode))
408 "Run the current project target in comint buffer."
409 (let ((tb (get-buffer-create " *padt*"))
410 (dd (oref obj path))
411 (cmd nil))
412 (unwind-protect
413 (progn
414 (set-buffer tb)
415 (setq default-directory dd)
416 (setq cmd (read-from-minibuffer
417 "Run (like this): "
418 (concat (ede-target-name obj))))
419 (ede-shell-run-something obj cmd))
420 (kill-buffer tb))))
421
422 (defmethod project-make-dist ((this project-am-target))
423 "Run the current project in the debugger."
424 (require 'compile)
425 (if (not project-am-compile-project-command)
426 (setq project-am-compile-project-command compile-command))
427 (project-compile-project this (concat project-am-compile-project-command
428 " dist")))
429
430 ;;; Project loading and saving
431 ;;
432 (defun project-am-load (project &optional rootproj)
433 "Read an automakefile PROJECT into our data structure.
434 Make sure that the tree down to our makefile is complete so that there
435 is cohesion in the project. Return the project file (or sub-project).
436 If a given set of projects has already been loaded, then do nothing
437 but return the project for the directory given.
438 Optional ROOTPROJ is the root EDE project."
439 ;; @TODO - rationalize this to the newer EDE way of doing things.
440 (setq project (expand-file-name project))
441 (let* ((ede-constructing t)
442 (fn (project-am-find-topmost-level (file-name-as-directory project)))
443 (amo nil)
444 (trimmed (if (string-match (regexp-quote fn)
445 project)
446 (replace-match "" t t project)
447 ""))
448 (subdir nil))
449 (setq amo (object-assoc (expand-file-name "Makefile.am" fn)
450 'file ede-projects))
451 (if amo
452 (error "Synchronous error in ede/project-am objects")
453 (let ((project-am-constructing t))
454 (setq amo (project-am-load-makefile fn))))
455 (if (not amo)
456 nil
457 ;; Now scan down from amo, and find the current directory
458 ;; from the PROJECT file.
459 (while (< 0 (length trimmed))
460 (if (string-match "\\([a-zA-Z0-9.-]+\\)/" trimmed)
461 (setq subdir (match-string 0 trimmed)
462 trimmed (replace-match "" t t trimmed))
463 (error "Error scanning down path for project"))
464 (setq amo (project-am-subtree
465 amo
466 (expand-file-name "Makefile.am"
467 (expand-file-name subdir fn)))
468 fn (expand-file-name subdir fn)))
469 amo)
470 ))
471
472 (defun project-am-find-topmost-level (dir)
473 "Find the topmost automakefile starting with DIR."
474 (let ((newdir dir))
475 (while (or (file-exists-p (concat newdir "Makefile.am"))
476 (file-exists-p (concat newdir "configure.ac"))
477 (file-exists-p (concat newdir "configure.in"))
478 )
479 (setq dir newdir newdir
480 (file-name-directory (directory-file-name newdir))))
481 (expand-file-name dir)))
482
483 (defmacro project-am-with-makefile-current (dir &rest forms)
484 "Set the Makefile.am in DIR to be the current buffer.
485 Run FORMS while the makefile is current.
486 Kill the makefile if it was not loaded before the load."
487 `(let* ((fn (expand-file-name "Makefile.am" ,dir))
488 (fb nil)
489 (kb (get-file-buffer fn)))
490 (if (not (file-exists-p fn))
491 nil
492 (save-excursion
493 (if kb (setq fb kb)
494 ;; We need to find-file this thing, but don't use
495 ;; any semantic features.
496 (let ((semantic-init-hook nil))
497 (setq fb (find-file-noselect fn)))
498 )
499 (set-buffer fb)
500 (prog1 ,@forms
501 (if (not kb) (kill-buffer (current-buffer))))))))
502 (put 'project-am-with-makefile-current 'lisp-indent-function 1)
503
504 (add-hook 'edebug-setup-hook
505 (lambda ()
506 (def-edebug-spec project-am-with-makefile-current
507 (form def-body))))
508
509
510 (defun project-am-load-makefile (path)
511 "Convert PATH into a project Makefile, and return its project object.
512 It does not check for existing project objects. Use `project-am-load'."
513 (project-am-with-makefile-current path
514 (if (and ede-object (project-am-makefile-p ede-object))
515 ede-object
516 (let* ((pi (project-am-package-info path))
517 (pn (or (nth 0 pi) (project-am-last-dir fn)))
518 (ver (or (nth 1 pi) "0.0"))
519 (bug (nth 2 pi))
520 (cof (nth 3 pi))
521 (ampf (project-am-makefile
522 pn :name pn
523 :version ver
524 :mailinglist (or bug "")
525 :file fn)))
526 (oset ampf :directory (file-name-directory fn))
527 (oset ampf configureoutputfiles cof)
528 (make-local-variable 'ede-object)
529 (setq ede-object ampf)
530 ;; Move the rescan after we set ede-object to prevent recursion
531 (project-rescan ampf)
532 ampf))))
533
534 ;;; Methods:
535 (defmethod ede-find-target ((amf project-am-makefile) buffer)
536 "Fetch the target belonging to BUFFER."
537 (or (call-next-method)
538 (let ((targ (oref amf targets))
539 (sobj (oref amf subproj))
540 (obj nil))
541 (while (and targ (not obj))
542 (if (ede-buffer-mine (car targ) buffer)
543 (setq obj (car targ)))
544 (setq targ (cdr targ)))
545 (while (and sobj (not obj))
546 (setq obj (project-am-buffer-object (car sobj) buffer)
547 sobj (cdr sobj)))
548 obj)))
549
550 (defmethod project-targets-for-file ((proj project-am-makefile))
551 "Return a list of targets the project PROJ."
552 (oref proj targets))
553
554 (defun project-am-scan-for-targets (currproj dir)
555 "Scan the current Makefile.am for targets.
556 CURRPROJ is the current project being scanned.
557 DIR is the directory to apply to new targets."
558 (let* ((otargets (oref currproj targets))
559 (ntargets nil)
560 (tmp nil)
561 )
562 (mapc
563 ;; Map all the different types
564 (lambda (typecar)
565 (let ((macro (nth 2 typecar))
566 (class (nth 1 typecar))
567 (indirect (nth 3 typecar))
568 ;(name (car typecar))
569 )
570 (if indirect
571 ;; Map all the found objects
572 (mapc (lambda (lstcar)
573 (setq tmp (object-assoc lstcar 'name otargets))
574 (when (not tmp)
575 (setq tmp (apply class lstcar :name lstcar
576 :path dir nil)))
577 (project-rescan tmp)
578 (setq ntargets (cons tmp ntargets)))
579 (makefile-macro-file-list macro))
580 ;; Non-indirect will have a target whos sources
581 ;; are actual files, not names of other targets.
582 (let ((files (makefile-macro-file-list macro)))
583 (when files
584 (setq tmp (object-assoc macro 'name otargets))
585 (when (not tmp)
586 (setq tmp (apply class macro :name macro
587 :path dir nil)))
588 (project-rescan tmp)
589 (setq ntargets (cons tmp ntargets))
590 ))
591 )
592 ))
593 project-am-type-alist)
594 ntargets))
595
596 (defmethod project-rescan ((this project-am-makefile))
597 "Rescan the makefile for all targets and sub targets."
598 (project-am-with-makefile-current (file-name-directory (oref this file))
599 ;;(message "Scanning %s..." (oref this file))
600 (let* ((pi (project-am-package-info (oref this directory)))
601 (pn (nth 0 pi))
602 (pv (nth 1 pi))
603 (bug (nth 2 pi))
604 (cof (nth 3 pi))
605 (osubproj (oref this subproj))
606 (csubproj (or
607 ;; If DIST_SUBDIRS doesn't exist, then go for the
608 ;; static list of SUBDIRS. The DIST version should
609 ;; contain SUBDIRS plus extra stuff.
610 (makefile-macro-file-list "DIST_SUBDIRS")
611 (makefile-macro-file-list "SUBDIRS")))
612 (csubprojexpanded nil)
613 (nsubproj nil)
614 ;; Targets are excluded here because they require
615 ;; special attention.
616 (dir (expand-file-name default-directory))
617 (tmp nil)
618 (ntargets (project-am-scan-for-targets this dir))
619 )
620
621 (and pn (string= (directory-file-name
622 (oref this directory))
623 (directory-file-name
624 (project-am-find-topmost-level
625 (oref this directory))))
626 (oset this name pn)
627 (and pv (oset this version pv))
628 (and bug (oset this mailinglist bug))
629 (oset this configureoutputfiles cof))
630
631 ; ;; LISP is different. Here there is only one kind of lisp (that I know of
632 ; ;; anyway) so it doesn't get mapped when it is found.
633 ; (if (makefile-move-to-macro "lisp_LISP")
634 ; (let ((tmp (project-am-lisp "lisp"
635 ; :name "lisp"
636 ; :path dir)))
637 ; (project-rescan tmp)
638 ; (setq ntargets (cons tmp ntargets))))
639 ;
640 ;; Now that we have this new list, chuck the old targets
641 ;; and replace it with the new list of targets I just created.
642 (oset this targets (nreverse ntargets))
643 ;; We still have a list of targets. For all buffers, make sure
644 ;; their object still exists!
645
646 ;; FIGURE THIS OUT
647
648 (mapc (lambda (sp)
649 (let ((var (makefile-extract-varname-from-text sp))
650 )
651 (if (not var)
652 (setq csubprojexpanded (cons sp csubprojexpanded))
653 ;; If it is a variable, expand that variable, and keep going.
654 (let ((varexp (makefile-macro-file-list var)))
655 (dolist (V varexp)
656 (setq csubprojexpanded (cons V csubprojexpanded)))))
657 ))
658 csubproj)
659
660 ;; Ok, now lets look at all our sub-projects.
661 (mapc (lambda (sp)
662 (let* ((subdir (file-name-as-directory
663 (expand-file-name
664 sp (file-name-directory (oref this :file)))))
665 (submake (expand-file-name
666 "Makefile.am"
667 subdir)))
668 (if (string= submake (oref this :file))
669 nil ;; don't recurse.. please!
670
671 ;; For each project id found, see if we need to recycle,
672 ;; and if we do not, then make a new one. Check the deep
673 ;; rescan value for behavior patterns.
674 (setq tmp (object-assoc
675 submake
676 'file osubproj))
677 (if (not tmp)
678 (setq tmp
679 (condition-case nil
680 ;; In case of problem, ignore it.
681 (project-am-load-makefile subdir)
682 (error nil)))
683 ;; If we have tmp, then rescan it only if deep mode.
684 (if ede-deep-rescan
685 (project-rescan tmp)))
686 ;; Tac tmp onto our list of things to keep, but only
687 ;; if tmp was found.
688 (when tmp
689 ;;(message "Adding %S" (object-print tmp))
690 (setq nsubproj (cons tmp nsubproj)))))
691 )
692 (nreverse csubprojexpanded))
693 (oset this subproj nsubproj)
694 ;; All elements should be updated now.
695 )))
696
697
698 (defmethod project-rescan ((this project-am-program))
699 "Rescan object THIS."
700 (oset this :source (makefile-macro-file-list (project-am-macro this)))
701 (oset this :ldadd (makefile-macro-file-list
702 (concat (oref this :name) "_LDADD"))))
703
704 (defmethod project-rescan ((this project-am-lib))
705 "Rescan object THIS."
706 (oset this :source (makefile-macro-file-list (project-am-macro this))))
707
708 (defmethod project-rescan ((this project-am-texinfo))
709 "Rescan object THIS."
710 (oset this :include (makefile-macro-file-list (project-am-macro this))))
711
712 (defmethod project-rescan ((this project-am-man))
713 "Rescan object THIS."
714 (oset this :source (makefile-macro-file-list (project-am-macro this))))
715
716 (defmethod project-rescan ((this project-am-lisp))
717 "Rescan the lisp sources."
718 (oset this :source (makefile-macro-file-list (project-am-macro this))))
719
720 (defmethod project-rescan ((this project-am-header))
721 "Rescan the Header sources for object THIS."
722 (oset this :source (makefile-macro-file-list (project-am-macro this))))
723
724 (defmethod project-rescan ((this project-am-built-src))
725 "Rescan built sources for object THIS."
726 (oset this :source (makefile-macro-file-list "BUILT_SOURCES")))
727
728 (defmethod project-rescan ((this project-am-extra-dist))
729 "Rescan object THIS."
730 (oset this :source (makefile-macro-file-list "EXTRA_DIST")))
731 ;; NOTE: The below calls 'file' then checks that it is some sort of
732 ;; text file. The file command may not be available on all platforms
733 ;; and some files may not exist yet. (ie - auto-generated)
734
735 ;;(mapc
736 ;; (lambda (f)
737 ;; ;; prevent garbage to be parsed, could we use :aux ?
738 ;; (if (and (not (member f (oref this :source)))
739 ;; (string-match-p "ASCII\\|text"
740 ;; (shell-command-to-string
741 ;; (concat "file " f))))
742 ;; (oset this :source (cons f (oref this :source)))))
743 ;; (makefile-macro-file-list "EXTRA_DIST")))
744
745 (defmethod project-am-macro ((this project-am-objectcode))
746 "Return the default macro to 'edit' for this object type."
747 (concat (subst-char-in-string ?- ?_ (oref this :name)) "_SOURCES"))
748
749 (defmethod project-am-macro ((this project-am-header-noinst))
750 "Return the default macro to 'edit' for this object."
751 "noinst_HEADERS")
752
753 (defmethod project-am-macro ((this project-am-header-inst))
754 "Return the default macro to 'edit' for this object."
755 "include_HEADERS")
756
757 (defmethod project-am-macro ((this project-am-header-pkg))
758 "Return the default macro to 'edit' for this object."
759 "pkginclude_HEADERS")
760
761 (defmethod project-am-macro ((this project-am-header-chk))
762 "Return the default macro to 'edit' for this object."
763 "check_HEADERS")
764
765 (defmethod project-am-macro ((this project-am-texinfo))
766 "Return the default macro to 'edit' for this object type."
767 (concat (file-name-sans-extension (oref this :name)) "_TEXINFOS"))
768
769 (defmethod project-am-macro ((this project-am-man))
770 "Return the default macro to 'edit' for this object type."
771 (oref this :name))
772
773 (defmethod project-am-macro ((this project-am-lisp))
774 "Return the default macro to 'edit' for this object."
775 "lisp_LISP")
776
777 (defun project-am-buffer-object (amf buffer)
778 "Return an object starting with AMF associated with BUFFER.
779 nil means that this buffer belongs to no-one."
780 (if (not amf)
781 nil
782 (if (ede-buffer-mine amf buffer)
783 amf
784 (let ((targ (oref amf targets))
785 (sobj (oref amf subproj))
786 (obj nil))
787 (while (and targ (not obj))
788 (if (ede-buffer-mine (car targ) buffer)
789 (setq obj (car targ)))
790 (setq targ (cdr targ)))
791 (while (and sobj (not obj))
792 (setq obj (project-am-buffer-object (car sobj) buffer)
793 sobj (cdr sobj)))
794 obj))))
795
796 (defmethod ede-buffer-mine ((this project-am-makefile) buffer)
797 "Return t if object THIS lays claim to the file in BUFFER."
798 (let ((efn (expand-file-name (buffer-file-name buffer))))
799 (or (string= (oref this :file) efn)
800 (string-match "/configure\\.ac$" efn)
801 (string-match "/configure\\.in$" efn)
802 (string-match "/configure$" efn)
803 ;; Search output files.
804 (let ((ans nil))
805 (dolist (f (oref this configureoutputfiles))
806 (when (string-match (concat (regexp-quote f) "$") efn)
807 (setq ans t)))
808 ans)
809 )))
810
811 (defmethod ede-buffer-mine ((this project-am-objectcode) buffer)
812 "Return t if object THIS lays claim to the file in BUFFER."
813 (member (file-name-nondirectory (buffer-file-name buffer))
814 (oref this :source)))
815
816 (defmethod ede-buffer-mine ((this project-am-texinfo) buffer)
817 "Return t if object THIS lays claim to the file in BUFFER."
818 (let ((bfn (buffer-file-name buffer)))
819 (or (string= (oref this :name) (file-name-nondirectory bfn))
820 (member (file-name-nondirectory bfn) (oref this :include)))))
821
822 (defmethod ede-buffer-mine ((this project-am-man) buffer)
823 "Return t if object THIS lays claim to the file in BUFFER."
824 (string= (oref this :name) (buffer-file-name buffer)))
825
826 (defmethod ede-buffer-mine ((this project-am-lisp) buffer)
827 "Return t if object THIS lays claim to the file in BUFFER."
828 (member (file-name-nondirectory (buffer-file-name buffer))
829 (oref this :source)))
830
831 (defmethod project-am-subtree ((ampf project-am-makefile) subdir)
832 "Return the sub project in AMPF specified by SUBDIR."
833 (object-assoc (expand-file-name subdir) 'file (oref ampf subproj)))
834
835 (defmethod project-compile-target-command ((this project-am-target))
836 "Default target to use when compiling a given target."
837 ;; This is a pretty good default for most.
838 "")
839
840 (defmethod project-compile-target-command ((this project-am-objectcode))
841 "Default target to use when compiling an object code target."
842 (oref this :name))
843
844 (defmethod project-compile-target-command ((this project-am-texinfo))
845 "Default target t- use when compling a texinfo file."
846 (let ((n (oref this :name)))
847 (if (string-match "\\.texi?\\(nfo\\)?" n)
848 (setq n (replace-match ".info" t t n)))
849 n))
850
851 \f
852 ;;; Generic useful functions
853
854 (defun project-am-last-dir (file)
855 "Return the last part of a directory name.
856 Argument FILE is the file to extract the end directory name from."
857 (let* ((s (file-name-directory file))
858 (d (directory-file-name s))
859 )
860 (file-name-nondirectory d))
861 )
862
863 (defun project-am-preferred-target-type (file)
864 "For FILE, return the preferred type for that file."
865 (cond ((string-match "\\.texi?\\(nfo\\)$" file)
866 project-am-texinfo)
867 ((string-match "\\.[0-9]$" file)
868 project-am-man)
869 ((string-match "\\.el$" file)
870 project-am-lisp)
871 (t
872 project-am-program)))
873
874 (defmethod ede-buffer-header-file((this project-am-objectcode) buffer)
875 "There are no default header files."
876 (or (call-next-method)
877 (let ((s (oref this source))
878 (found nil))
879 (while (and s (not found))
880 ;; Add more logic here if applicable.
881 (if (string-match "\\.\\(h\\|H\\|hh\\|hpp\\)" (car s))
882 (setq found (car s)))
883 (setq s (cdr s)))
884 found)))
885
886 (defmethod ede-documentation ((this project-am-texinfo))
887 "Return a list of files that provides documentation.
888 Documentation is not for object THIS, but is provided by THIS for other
889 files in the project."
890 (let* ((src (append (oref this source)
891 (oref this include)))
892 (proj (ede-target-parent this))
893 (dir (oref proj directory))
894 (out nil))
895 ;; Loop over all entries and expand
896 (while src
897 (setq out (cons
898 (expand-file-name (car src) dir)
899 out))
900 (setq src (cdr src)))
901 ;; return it
902 out))
903
904
905 ;;; Configure.in queries.
906 ;;
907 (defvar project-am-autoconf-file-options
908 '("configure.in" "configure.ac")
909 "List of possible configure files to look in for project info.")
910
911 (defun project-am-autoconf-file (dir)
912 "Return the name of the autoconf file to use in DIR."
913 (let ((ans nil))
914 (dolist (L project-am-autoconf-file-options)
915 (when (file-exists-p (expand-file-name L dir))
916 (setq ans (expand-file-name L dir))))
917 ans))
918
919 (defmacro project-am-with-config-current (file &rest forms)
920 "Set the Configure FILE in the top most directory above DIR as current.
921 Run FORMS in the configure file.
922 Kill the Configure buffer if it was not already in a buffer."
923 `(save-excursion
924 (let ((fb (generate-new-buffer ,file)))
925 (set-buffer fb)
926 (erase-buffer)
927 (insert-file-contents ,file)
928 (prog1 ,@forms
929 (kill-buffer fb)))))
930
931 (put 'project-am-with-config-current 'lisp-indent-function 1)
932
933 (add-hook 'edebug-setup-hook
934 (lambda ()
935 (def-edebug-spec project-am-with-config-current
936 (form def-body))))
937
938 (defmacro project-am-extract-shell-variable (var)
939 "Extract the value of the shell variable VAR from a shell script."
940 (save-excursion
941 (goto-char (point-min))
942 (when (re-search-forward (concat "^" (regexp-quote var) "\\s-*=\\s-*")
943 nil t)
944 (buffer-substring-no-properties (point) (point-at-eol)))))
945
946 (defun project-am-extract-package-info (dir)
947 "Extract the package information for directory DIR."
948 (let ((conf-in (project-am-autoconf-file dir))
949 (conf-sh (expand-file-name "configure" dir))
950 (name (file-name-nondirectory
951 (directory-file-name dir)))
952 (ver "1.0")
953 (bugrep nil)
954 (configfiles nil)
955 )
956 (cond
957 ;; Try configure.in or configure.ac
958 (conf-in
959 (require 'ede/autoconf-edit)
960 (project-am-with-config-current conf-in
961 (let ((aci (autoconf-parameters-for-macro "AC_INIT"))
962 (aia (autoconf-parameters-for-macro "AM_INIT_AUTOMAKE"))
963 (acf (autoconf-parameters-for-macro "AC_CONFIG_FILES"))
964 (aco (autoconf-parameters-for-macro "AC_OUTPUT"))
965 )
966 (cond
967 ;; AC init has more than 1 parameter
968 ((> (length aci) 1)
969 (setq name (nth 0 aci)
970 ver (nth 1 aci)
971 bugrep (nth 2 aci)))
972 ;; The init automake has more than 1 parameter
973 ((> (length aia) 1)
974 (setq name (nth 0 aia)
975 ver (nth 1 aia)
976 bugrep (nth 2 aia)))
977 )
978 ;; AC_CONFIG_FILES, or AC_OUTPUT lists everything that
979 ;; should be detected as part of this PROJECT, but not in a
980 ;; particular TARGET.
981 (let ((outfiles (cond (aco (list (car aco)))
982 (t acf))))
983 (if (> (length outfiles) 1)
984 (setq configfiles outfiles)
985 (setq configfiles (split-string (car outfiles) " " t)))
986 )
987 ))
988 )
989 ;; Else, try the script
990 ((file-exists-p conf-sh)
991 (project-am-with-config-current conf-sh
992 (setq name (project-am-extract-shell-variable "PACKAGE_NAME")
993 ver (project-am-extract-shell-variable "PACKAGE_VERSION")
994 )
995 ))
996 ;; Don't know what else....
997 (t
998 nil))
999 ;; Return stuff
1000 (list name ver bugrep configfiles)
1001 ))
1002
1003 (defun project-am-package-info (dir)
1004 "Get the package information for directory topmost project dir over DIR.
1005 Calculates the info with `project-am-extract-package-info'."
1006 (let ((top (ede-toplevel)))
1007 (when top (setq dir (oref top :directory)))
1008 (project-am-extract-package-info dir)))
1009
1010 (provide 'ede/project-am)
1011
1012 ;; arch-tag: 528db935-f186-4240-b647-e305c5b784a2
1013 ;;; ede/project-am.el ends here