]> code.delx.au - gnu-emacs/blob - lisp/cedet/ede/project-am.el
Convert consecutive FSF copyright years to ranges.
[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-2000, 2003, 2005, 2007-2011
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 its base template, and then maintains that
34 ;; information during edits, automatically updating the automake file
35 ;; where appropriate.
36
37 (require 'make-mode)
38 (require 'ede)
39 (require 'ede/make)
40 (require 'ede/makefile-edit)
41 (require 'semantic/find) ;; for semantic-find-tags-by-...
42 (require 'ede/autoconf-edit)
43
44 (declare-function autoconf-parameters-for-macro "ede/autoconf-edit")
45 (declare-function ede-shell-run-something "ede/shell")
46 (eval-when-compile (require 'compile))
47
48 ;;; Code:
49 (defgroup project-am nil
50 "File and tag browser frame."
51 :group 'tools
52 :group 'ede
53 )
54
55 (defcustom project-am-compile-project-command nil
56 "*Default command used to compile a project."
57 :group 'project-am
58 :type 'string)
59
60 (defcustom project-am-compile-target-command (concat ede-make-command " -k %s")
61 "*Default command used to compile a project."
62 :group 'project-am
63 :type 'string)
64
65 (defcustom project-am-debug-target-function 'gdb
66 "*Default Emacs command used to debug a target."
67 :group 'project-am
68 :type 'sexp) ; make this be a list some day
69
70 (defconst project-am-type-alist
71 '(("bin" project-am-program "bin_PROGRAMS" t)
72 ("sbin" project-am-program "sbin_PROGRAMS" t)
73 ("noinstbin" project-am-program "noinst_PROGRAMS" t)
74 ("checkbin" project-am-program "check_PROGRAMS" t)
75 ("lib" project-am-lib "lib_LIBS" t)
76 ("libraries" project-am-lib "lib_LIBRARIES" t)
77 ("librariesnoinst" project-am-lib "noinst_LIBRARIES" t)
78 ("pkglibraries" project-am-lib "pkglib_LIBRARIES" t)
79 ("checklibs" project-am-lib "check_LIBRARIES" t)
80 ("ltlibraries" project-am-lib "lib_LTLIBRARIES" t)
81 ("ltlibrariesnoinst" project-am-lib "noinst_LTLIBRARIES" t)
82 ("pkgltlibraries" project-am-lib "pkglib_LTLIBRARIES" t)
83 ("checkltlibs" project-am-lib "check_LTLIBRARIES" t)
84 ("headernoinst" project-am-header-noinst "noinst_HEADERS")
85 ("headerinst" project-am-header-inst "include_HEADERS")
86 ("headerpkg" project-am-header-pkg "pkginclude_HEADERS")
87 ("headerpkg" project-am-header-chk "check_HEADERS")
88 ("texinfo" project-am-texinfo "info_TEXINFOS" t)
89 ("man" project-am-man "man_MANS")
90 ("lisp" project-am-lisp "lisp_LISP")
91 ;; for other global files track EXTRA_
92 ("extrabin" project-am-program "EXTRA_PROGRAMS" t)
93 ("builtsrcs" project-am-built-src "BUILT_SOURCES")
94 ("extradist" project-am-extra-dist "EXTRA_DIST")
95 ;; Custom libraries targets?
96 ;; ("ltlibcustom" project-am-lib ".*?_LTLIBRARIES" t)
97 )
98 "Alist of type names and the type of object to create for them.
99 Each entry is of the form:
100 (EMACSNAME CLASS AUTOMAKEVAR INDIRECT)
101 where EMACSNAME is a name for Emacs to use.
102 CLASS is the EDE target class to represent the target.
103 AUTOMAKEVAR is the Automake variable to identify. This cannot be a
104 regular expression.
105 INDIRECT is optional. If it is non-nil, then the variable in
106 question lists other variables that need to be looked up.")
107
108
109 (defconst project-am-meta-type-alist
110 '((project-am-program "_PROGRAMS$" t)
111 (project-am-lib "_\\(LIBS\\|LIBRARIES\\|LTLIBRARIES\\)$" t)
112
113 ;; direct primary target use a dummy object (man target)
114 ;; update to: * 3.3 Uniform in automake-1.11 info node.
115 (project-am-man "_\\(DATA\\|HEADERS\\|PYTHON\\|JAVA\\|SCRIPTS\\|MANS\\|TEXINFOS\\)$" nil)
116 )
117 "Alist of meta-target type, each entry has form:
118 (CLASS REGEXPVAR INDIRECT)
119 where CLASS is the EDE target class for target.
120 REGEXPVAR is the regexp used in `semantic-find-tags-by-name-regexp'.
121 INDIRECT is optional. If it is non-nil, then the variable in it have
122 other meta-variable based on this name.")
123
124
125 (defclass project-am-target (ede-target)
126 nil
127 "Base target class for everything in project-am.")
128
129 (defclass project-am-objectcode (project-am-target)
130 ((source :initarg :source :documentation "List of source files."))
131 "A target which creates object code, like a C program or library.")
132
133 (defclass project-am-program (project-am-objectcode)
134 ((ldadd :initarg :ldadd :documentation "Additional LD args."
135 :initform nil))
136 "A top level program to build")
137
138 (defclass project-am-header (project-am-target)
139 ()
140 "A group of misc source files, such as headers.")
141
142 (defclass project-am-header-noinst (project-am-header)
143 ()
144 "A group of header files that are not installed.")
145
146 (defclass project-am-header-inst (project-am-header)
147 ()
148 "A group of header files that are not installed.")
149
150 (defclass project-am-header-pkg (project-am-header)
151 ()
152 "A group of header files that are not installed.")
153
154 (defclass project-am-header-chk (project-am-header)
155 ()
156 "A group of header files that are not installed.")
157
158 (defclass project-am-lib (project-am-objectcode)
159 nil
160 "A top level library to build")
161
162 (defclass project-am-lisp (project-am-target)
163 ()
164 "A group of Emacs Lisp programs to byte compile.")
165
166 (defclass project-am-texinfo (project-am-target)
167 ((include :initarg :include
168 :initform nil
169 :documentation "Additional texinfo included in this one."))
170 "A top level texinfo file to build.")
171
172 (defclass project-am-man (project-am-target)
173 nil
174 "A top level man file to build.")
175
176 ;; For generic files tracker like EXTRA_DIST
177 (defclass project-am-built-src (project-am-target)
178 ()
179 "A group of Emacs Lisp programs to byte compile.")
180
181 (defclass project-am-extra-dist (project-am-target)
182 ()
183 "A group of Emacs Lisp programs to byte compile.")
184
185 (defclass project-am-makefile (ede-project)
186 ((targets :initarg :targets
187 :initform nil
188 :documentation "Top level targets in this makefile.")
189 (configureoutputfiles
190 :initform nil
191 :documentation
192 "List of files output from configure system.")
193 )
194 "Encode one makefile.")
195
196 ;;; Code:
197 (defmethod project-add-file ((ot project-am-target))
198 "Add the current buffer into a project.
199 OT is the object target. DIR is the directory to start in."
200 (let* ((target (if ede-object (error "Already associated w/ a target")
201 (let ((amf (project-am-load default-directory)))
202 (if (not amf) (error "No project file"))
203 (completing-read "Target: "
204 (object-assoc-list 'name
205 (oref amf targets))
206 nil t))))
207 ;; The input target might be new. See if we can find it.
208 (amf (ede-load-project-file (oref ot path)))
209 (ot (object-assoc target 'name (oref amf targets)))
210 (ofn (file-name-nondirectory (buffer-file-name))))
211 (if (not ot)
212 (setq ot
213 (project-new-target
214 target (project-am-preferred-target-type (buffer-file-name)))))
215 (ede-with-projectfile ot
216 (makefile-move-to-macro (project-am-macro ot))
217 (ede-maybe-checkout)
218 (makefile-end-of-command)
219 (insert " " ofn)
220 (makefile-fill-paragraph nil)
221 (project-rescan ot)
222 (save-buffer))
223 (setq ede-object ot)))
224
225 (defmethod project-remove-file ((ot project-am-target) fnnd)
226 "Remove the current buffer from any project targets."
227 (ede-with-projectfile ot
228 (makefile-move-to-macro (project-am-macro ot))
229 (if (and buffer-read-only vc-mode
230 (y-or-n-p "Checkout Makefile.am from VC? "))
231 (vc-toggle-read-only t))
232 (ede-maybe-checkout)
233 (makefile-navigate-macro (concat " *" (regexp-quote (ede-name fnnd))))
234 (replace-match "" t t nil 0)
235 (makefile-fill-paragraph nil)
236 (project-rescan ot)
237 (save-buffer))
238 (setq ede-object nil))
239
240 (defmethod project-edit-file-target ((obj project-am-target))
241 "Edit the target associated w/ this file."
242 (find-file (concat (oref obj path) "Makefile.am"))
243 (goto-char (point-min))
244 (makefile-move-to-macro (project-am-macro obj))
245 (if (= (point-min) (point))
246 (re-search-forward (ede-target-name obj))))
247
248 (defmethod project-new-target ((proj project-am-makefile)
249 &optional name type)
250 "Create a new target named NAME.
251 Argument TYPE is the type of target to insert. This is a string
252 matching something in `project-am-type-alist' or type class symbol.
253 Despite the fact that this is a method, it depends on the current
254 buffer being in order to provide a smart default target type."
255 (let* ((name (or name (read-string "Name: " "")))
256 (type (or type
257 (completing-read "Type: "
258 project-am-type-alist
259 nil t
260 (cond ((eq major-mode 'texinfo-mode)
261 "texinfo")
262 ((eq major-mode 'nroff-mode)
263 "man")
264 ((eq major-mode 'emacs-lisp-mode)
265 "lisp")
266 (t "bin")))))
267 (ntype (assoc type project-am-type-alist))
268 (ot nil))
269 (setq ot (apply (car (cdr ntype)) name :name name
270 :path (expand-file-name default-directory) nil))
271 (if (not ot) (error "Error creating target object %S" ntype))
272 (ede-with-projectfile ot
273 (goto-char (point-min))
274 (ede-maybe-checkout)
275 (makefile-next-dependency)
276 (if (= (point) (point-min))
277 (goto-char (point-max))
278 (beginning-of-line)
279 (insert "\n")
280 (forward-char -1))
281 ;; Add the new target sources macro (if needed)
282 (if (project-am-macro ot)
283 (makefile-insert-macro (project-am-macro ot)))
284 ;; Add to the list of objects.
285 (goto-char (point-min))
286 (makefile-move-to-macro (car (cdr (cdr ntype))))
287 (if (= (point) (point-min))
288 (progn
289 (if (re-search-forward makefile-macroassign-regex nil t)
290 (progn (forward-line -1)
291 (end-of-line)
292 (insert "\n"))
293 ;; If the above search fails, thats ok. We'd just want to be at
294 ;; point-min anyway.
295 )
296 (makefile-insert-macro (car (cdr (cdr ntype))))))
297 (makefile-end-of-command)
298 (insert " " (ede-target-name ot))
299 (save-buffer)
300 ;; Rescan the object in this makefile.
301 (project-rescan ede-object))))
302
303 ;;
304 ;; NOTE TO SELF
305 ;;
306 ;; This should be handled at the EDE level, calling a method of the
307 ;; top most project.
308 ;;
309 (defmethod project-compile-project ((obj project-am-target) &optional command)
310 "Compile the entire current project.
311 Argument COMMAND is the command to use when compiling."
312 (require 'compile)
313 (if (not command)
314 (setq
315 command
316 ;; This interactive statement was taken from compile, and I'll
317 ;; use the same command history too.
318 (progn
319 (if (not project-am-compile-project-command)
320 (setq project-am-compile-project-command compile-command))
321 (if (or compilation-read-command current-prefix-arg)
322 (read-from-minibuffer "Project compile command: "
323 ;; hardcode make -k
324 ;; This is compile project after all.
325 project-am-compile-project-command
326 nil nil '(compile-history . 1))
327 project-am-compile-project-command))))
328 ;; When compile a project, we might be in a subdirectory,
329 ;; so we have to make sure we move all the way to the top.
330 (let* ((default-directory (project-am-find-topmost-level default-directory)))
331 (compile command)))
332
333 (defmethod project-compile-project ((obj project-am-makefile)
334 &optional command)
335 "Compile the entire current project.
336 Argument COMMAND is the command to use when compiling."
337 (require 'compile)
338 (if (not command)
339 (setq
340 command
341 ;; This interactive statement was taken from compile, and I'll
342 ;; use the same command history too.
343 (progn
344 (if (not project-am-compile-project-command)
345 (setq project-am-compile-project-command compile-command))
346 (if (or compilation-read-command current-prefix-arg)
347 (read-from-minibuffer "Project compile command: "
348 ;; hardcode make -k
349 ;; This is compile project after all.
350 project-am-compile-project-command
351 nil nil '(compile-history . 1))
352 project-am-compile-project-command))))
353 ;; When compile a project, we might be in a subdirectory,
354 ;; so we have to make sure we move all the way to the top.
355 (let* ((default-directory (project-am-find-topmost-level default-directory)))
356 (compile command)))
357
358 (defmethod project-compile-target ((obj project-am-target) &optional command)
359 "Compile the current target.
360 Argument COMMAND is the command to use for compiling the target."
361 (require 'compile)
362 (if (not project-am-compile-project-command)
363 (setq project-am-compile-project-command compile-command))
364 (if (not command)
365 (setq
366 command
367 (if compilation-read-command
368 (read-from-minibuffer "Project compile command: "
369 ;; hardcode make -k
370 ;; This is compile project after all.
371 (if ede-object
372 (format
373 project-am-compile-target-command
374 (project-compile-target-command
375 ede-object))
376 project-am-compile-target-command)
377 nil nil
378 '(compile-history . 1))
379 (if ede-object
380 project-am-compile-project-command
381 (format
382 project-am-compile-target-command
383 (project-compile-target-command ede-object))))))
384 ;; We better be in the right place when compiling a specific target.
385 (compile command))
386
387 (defmethod project-debug-target ((obj project-am-objectcode))
388 "Run the current project target in a debugger."
389 (let ((tb (get-buffer-create " *padt*"))
390 (dd (oref obj path))
391 (cmd nil))
392 (unwind-protect
393 (progn
394 (require 'ede/shell)
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 (declare-function ede-shell-run-something "ede/shell")
405
406 (defmethod project-run-target ((obj project-am-objectcode))
407 "Run the current project target in comint buffer."
408 (require 'ede/shell)
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 (directory &optional rootproj)
433 "Read an automakefile DIRECTORY into our data structure.
434 If a given set of projects has already been loaded, then do nothing
435 but return the project for the directory given.
436 Optional ROOTPROJ is the root EDE project."
437 (let* ((ede-constructiong t)
438 (amo (object-assoc (expand-file-name "Makefile.am" directory)
439 'file ede-projects)))
440 (when (not amo)
441 (setq amo (project-am-load-makefile directory)))
442 amo))
443
444 (defun project-am-find-topmost-level (dir)
445 "Find the topmost automakefile starting with DIR."
446 (let ((newdir dir))
447 (while (or (file-exists-p (concat newdir "Makefile.am"))
448 (file-exists-p (concat newdir "configure.ac"))
449 (file-exists-p (concat newdir "configure.in"))
450 )
451 (setq dir newdir newdir
452 (file-name-directory (directory-file-name newdir))))
453 (expand-file-name dir)))
454
455 (defmacro project-am-with-makefile-current (dir &rest forms)
456 "Set the Makefile.am in DIR to be the current buffer.
457 Run FORMS while the makefile is current.
458 Kill the makefile if it was not loaded before the load."
459 `(let* ((fn (expand-file-name "Makefile.am" ,dir))
460 (fb nil)
461 (kb (get-file-buffer fn)))
462 (if (not (file-exists-p fn))
463 nil
464 (save-excursion
465 (if kb (setq fb kb)
466 ;; We need to find-file this thing, but don't use
467 ;; any semantic features.
468 (let ((semantic-init-hook nil)
469 (recentf-exclude '( (lambda (f) t) ))
470 )
471 (setq fb (find-file-noselect fn)))
472 )
473 (set-buffer fb)
474 (prog1 ,@forms
475 (if (not kb) (kill-buffer (current-buffer))))))))
476 (put 'project-am-with-makefile-current 'lisp-indent-function 1)
477
478 (add-hook 'edebug-setup-hook
479 (lambda ()
480 (def-edebug-spec project-am-with-makefile-current
481 (form def-body))))
482
483
484 (defun project-am-load-makefile (path &optional suggestedname)
485 "Convert PATH into a project Makefile, and return its project object.
486 It does not check for existing project objects. Use `project-am-load'.
487 Optional argument SUGGESTEDNAME will be the project name.
488 This is used when subprojects are made in named subdirectories."
489 (project-am-with-makefile-current path
490 (if (and ede-object (project-am-makefile-p ede-object))
491 ede-object
492 (let* ((pi (project-am-package-info path))
493 (sfn (when suggestedname
494 (project-am-last-dir suggestedname)))
495 (pn (or sfn (nth 0 pi) (project-am-last-dir fn)))
496 (ver (or (nth 1 pi) "0.0"))
497 (bug (nth 2 pi))
498 (cof (nth 3 pi))
499 (ampf (project-am-makefile
500 pn :name pn
501 :version ver
502 :mailinglist (or bug "")
503 :file fn)))
504 (oset ampf :directory (file-name-directory fn))
505 (oset ampf configureoutputfiles cof)
506 (make-local-variable 'ede-object)
507 (setq ede-object ampf)
508 ;; Move the rescan after we set ede-object to prevent recursion
509 (project-rescan ampf)
510 ampf))))
511
512 ;;; Methods:
513 (defmethod project-targets-for-file ((proj project-am-makefile))
514 "Return a list of targets the project PROJ."
515 (oref proj targets))
516
517 (defun project-am-scan-for-targets (currproj dir)
518 "Scan the current Makefile.am for targets.
519 CURRPROJ is the current project being scanned.
520 DIR is the directory to apply to new targets."
521 (let* ((otargets (oref currproj targets))
522 ;; `ntargets' results in complete targets list
523 ;; not only the new targets by diffing.
524 (ntargets nil)
525 (tmp nil)
526 )
527
528 (mapc
529 ;; Map all the different types
530 (lambda (typecar)
531 (let ((macro (nth 2 typecar))
532 (class (nth 1 typecar))
533 (indirect (nth 3 typecar))
534 )
535 (if indirect
536 ;; Map all the found objects
537 (mapc (lambda (lstcar)
538 (setq tmp (object-assoc lstcar 'name otargets))
539 (when (not tmp)
540 (setq tmp (apply class lstcar :name lstcar
541 :path dir nil)))
542 (project-rescan tmp)
543 (setq ntargets (cons tmp ntargets)))
544 (makefile-macro-file-list macro))
545 ;; Non-indirect will have a target whos sources
546 ;; are actual files, not names of other targets.
547 (let ((files (makefile-macro-file-list macro)))
548 (when files
549 (setq tmp (object-assoc macro 'name otargets))
550 (when (not tmp)
551 (setq tmp (apply class macro :name macro
552 :path dir nil)))
553 (project-rescan tmp)
554 (setq ntargets (cons tmp ntargets))
555 ))
556 )
557 ))
558 project-am-type-alist)
559
560 ;; At now check variables for meta-target regexp
561 ;; We have to check ntargets to avoid useless rescan.
562 ;; Also we have check otargets to prevent duplication.
563 (mapc
564 (lambda (typecar)
565 (let ((class (nth 0 typecar))
566 (metaregex (nth 1 typecar))
567 (indirect (nth 2 typecar)))
568 (if indirect
569 ;; Map all the found objects
570 (mapc
571 (lambda (lstcar)
572 (unless (object-assoc lstcar 'name ntargets)
573 (or
574 (setq tmp (object-assoc lstcar 'name otargets))
575 (setq tmp (apply class lstcar :name lstcar
576 :path dir nil)))
577 (project-rescan tmp)
578 (setq ntargets (cons tmp ntargets))))
579 ;; build a target list to map over
580 (let (atargets)
581 (dolist (TAG
582 (semantic-find-tags-by-name-regexp
583 metaregex (semantic-find-tags-by-class
584 'variable (semantic-fetch-tags))))
585 ;; default-value have to be a list
586 (when (cadr (assoc ':default-value TAG))
587 (setq atargets
588 (append
589 (nreverse (cadr (assoc ':default-value TAG)))
590 atargets))))
591 (nreverse atargets)))
592
593 ;; else not indirect, TODO: FIX various direct meta type in a sane way.
594 (dolist (T (semantic-find-tags-by-name-regexp
595 metaregex (semantic-find-tags-by-class
596 'variable (semantic-fetch-tags))))
597 (unless (setq tmp (object-assoc (car T) 'name ntargets))
598 (or (setq tmp (object-assoc (car T) 'name otargets))
599 ;; we are really new
600 (setq tmp (apply class (car T) :name (car T)
601 :path dir nil)))
602 (project-rescan tmp)
603 (setq ntargets (cons tmp ntargets))))
604 )))
605 project-am-meta-type-alist)
606 ntargets))
607
608 (defun project-am-expand-subdirlist (place subdirs)
609 "Store in PLACE the SUBDIRS expanded from variables.
610 Strip out duplicates, and recurse on variables."
611 (mapc (lambda (sp)
612 (let ((var (makefile-extract-varname-from-text sp)))
613 (if var
614 ;; If it is a variable, expand that variable, and keep going.
615 (project-am-expand-subdirlist
616 place (makefile-macro-file-list var))
617 ;; Else, add SP in if it isn't a dup.
618 (if (member sp (symbol-value place))
619 nil ; don't do it twice.
620 (set place (cons sp (symbol-value place))) ;; add
621 ))))
622 subdirs)
623 )
624
625 (defmethod project-rescan ((this project-am-makefile) &optional suggestedname)
626 "Rescan the makefile for all targets and sub targets."
627 (project-am-with-makefile-current (file-name-directory (oref this file))
628 ;;(message "Scanning %s..." (oref this file))
629 (let* ((pi (project-am-package-info (oref this directory)))
630 (pn (nth 0 pi))
631 (pv (nth 1 pi))
632 (bug (nth 2 pi))
633 (cof (nth 3 pi))
634 (osubproj (oref this subproj))
635 ;; 1/30/10 - We need to append these two lists together,
636 ;; then strip out duplicates. Expanding this list (via
637 ;; references to other variables should also strip out dups
638 (csubproj (append
639 (makefile-macro-file-list "DIST_SUBDIRS")
640 (makefile-macro-file-list "SUBDIRS")))
641 (csubprojexpanded nil)
642 (nsubproj nil)
643 ;; Targets are excluded here because they require
644 ;; special attention.
645 (dir (expand-file-name default-directory))
646 (tmp nil)
647 (ntargets (project-am-scan-for-targets this dir))
648 )
649 (if suggestedname
650 (oset this name (project-am-last-dir suggestedname))
651 ;; Else, setup toplevel project info.
652 (and pn (string= (directory-file-name
653 (oref this directory))
654 (directory-file-name
655 (project-am-find-topmost-level
656 (oref this directory))))
657 (oset this name pn)
658 (and pv (oset this version pv))
659 (and bug (oset this mailinglist bug))
660 (oset this configureoutputfiles cof)))
661 ;; Now that we have this new list, chuck the old targets
662 ;; and replace it with the new list of targets I just created.
663 (oset this targets (nreverse ntargets))
664 ;; We still have a list of targets. For all buffers, make sure
665 ;; their object still exists!
666 ;; FIGURE THIS OUT
667 (project-am-expand-subdirlist 'csubprojexpanded csubproj)
668 ;; Ok, now lets look at all our sub-projects.
669 (mapc (lambda (sp)
670 (let* ((subdir (file-name-as-directory
671 (expand-file-name
672 sp (file-name-directory (oref this :file)))))
673 (submake (expand-file-name
674 "Makefile.am"
675 subdir)))
676 (if (string= submake (oref this :file))
677 nil ;; don't recurse.. please!
678 ;; For each project id found, see if we need to recycle,
679 ;; and if we do not, then make a new one. Check the deep
680 ;; rescan value for behavior patterns.
681 (setq tmp (object-assoc
682 submake
683 'file osubproj))
684 (if (not tmp)
685 (setq tmp
686 (condition-case nil
687 ;; In case of problem, ignore it.
688 (project-am-load-makefile subdir subdir)
689 (error nil)))
690 ;; If we have tmp, then rescan it only if deep mode.
691 (if ede-deep-rescan
692 (project-rescan tmp subdir)))
693 ;; Tac tmp onto our list of things to keep, but only
694 ;; if tmp was found.
695 (when tmp
696 ;;(message "Adding %S" (object-print tmp))
697 (setq nsubproj (cons tmp nsubproj)))))
698 )
699 (nreverse csubprojexpanded))
700 (oset this subproj nsubproj)
701 ;; All elements should be updated now.
702 )))
703
704
705 (defmethod project-rescan ((this project-am-program))
706 "Rescan object THIS."
707 (oset this :source (makefile-macro-file-list (project-am-macro this)))
708 (unless (oref this :source)
709 (oset this :source (list (concat (oref this :name) ".c"))))
710 (oset this :ldadd (makefile-macro-file-list
711 (concat (oref this :name) "_LDADD"))))
712
713 (defmethod project-rescan ((this project-am-lib))
714 "Rescan object THIS."
715 (oset this :source (makefile-macro-file-list (project-am-macro this)))
716 (unless (oref this :source)
717 (oset this :source (list (concat (file-name-sans-extension (oref this :name)) ".c")))))
718
719 (defmethod project-rescan ((this project-am-texinfo))
720 "Rescan object THIS."
721 (oset this :include (makefile-macro-file-list (project-am-macro this))))
722
723 (defmethod project-rescan ((this project-am-man))
724 "Rescan object THIS."
725 (oset this :source (makefile-macro-file-list (project-am-macro this))))
726
727 (defmethod project-rescan ((this project-am-lisp))
728 "Rescan the lisp sources."
729 (oset this :source (makefile-macro-file-list (project-am-macro this))))
730
731 (defmethod project-rescan ((this project-am-header))
732 "Rescan the Header sources for object THIS."
733 (oset this :source (makefile-macro-file-list (project-am-macro this))))
734
735 (defmethod project-rescan ((this project-am-built-src))
736 "Rescan built sources for object THIS."
737 (oset this :source (makefile-macro-file-list "BUILT_SOURCES")))
738
739 (defmethod project-rescan ((this project-am-extra-dist))
740 "Rescan object THIS."
741 (oset this :source (makefile-macro-file-list "EXTRA_DIST")))
742
743 (defmethod project-am-macro ((this project-am-objectcode))
744 "Return the default macro to 'edit' for this object type."
745 (concat (subst-char-in-string ?- ?_ (oref this :name)) "_SOURCES"))
746
747 (defmethod project-am-macro ((this project-am-header-noinst))
748 "Return the default macro to 'edit' for this object."
749 "noinst_HEADERS")
750
751 (defmethod project-am-macro ((this project-am-header-inst))
752 "Return the default macro to 'edit' for this object."
753 "include_HEADERS")
754
755 (defmethod project-am-macro ((this project-am-header-pkg))
756 "Return the default macro to 'edit' for this object."
757 "pkginclude_HEADERS")
758
759 (defmethod project-am-macro ((this project-am-header-chk))
760 "Return the default macro to 'edit' for this object."
761 "check_HEADERS")
762
763 (defmethod project-am-macro ((this project-am-texinfo))
764 "Return the default macro to 'edit' for this object type."
765 (concat (file-name-sans-extension (oref this :name)) "_TEXINFOS"))
766
767 (defmethod project-am-macro ((this project-am-man))
768 "Return the default macro to 'edit' for this object type."
769 (oref this :name))
770
771 (defmethod project-am-macro ((this project-am-lisp))
772 "Return the default macro to 'edit' for this object."
773 "lisp_LISP")
774
775 (defun project-am-buffer-object (amf buffer)
776 "Return an object starting with AMF associated with BUFFER.
777 nil means that this buffer belongs to no-one."
778 (if (not amf)
779 nil
780 (if (ede-buffer-mine amf buffer)
781 amf
782 (let ((targ (oref amf targets))
783 (sobj (oref amf subproj))
784 (obj nil))
785 (while (and targ (not obj))
786 (if (ede-buffer-mine (car targ) buffer)
787 (setq obj (car targ)))
788 (setq targ (cdr targ)))
789 (while (and sobj (not obj))
790 (setq obj (project-am-buffer-object (car sobj) buffer)
791 sobj (cdr sobj)))
792 obj))))
793
794 (defmethod ede-buffer-mine ((this project-am-makefile) buffer)
795 "Return t if object THIS lays claim to the file in BUFFER."
796 (let ((efn (expand-file-name (buffer-file-name buffer))))
797 (or (string= (oref this :file) efn)
798 (string-match "/configure\\.ac$" efn)
799 (string-match "/configure\\.in$" efn)
800 (string-match "/configure$" efn)
801 ;; Search output files.
802 (let ((ans nil))
803 (dolist (f (oref this configureoutputfiles))
804 (when (string-match (concat (regexp-quote f) "$") efn)
805 (setq ans t)))
806 ans)
807 )))
808
809 (defmethod ede-buffer-mine ((this project-am-objectcode) buffer)
810 "Return t if object THIS lays claim to the file in BUFFER."
811 (member (file-relative-name (buffer-file-name buffer) (oref this :path))
812 (oref this :source)))
813
814 (defmethod ede-buffer-mine ((this project-am-texinfo) buffer)
815 "Return t if object THIS lays claim to the file in BUFFER."
816 (let ((bfn (file-relative-name (buffer-file-name buffer)
817 (oref this :path))))
818 (or (string= (oref this :name) bfn)
819 (member bfn (oref this :include)))))
820
821 (defmethod ede-buffer-mine ((this project-am-man) buffer)
822 "Return t if object THIS lays claim to the file in BUFFER."
823 (string= (oref this :name)
824 (file-relative-name (buffer-file-name buffer) (oref this :path))))
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-relative-name (buffer-file-name buffer) (oref this :path))
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 (project-am-with-config-current conf-in
960 (let ((aci (autoconf-parameters-for-macro "AC_INIT"))
961 (aia (autoconf-parameters-for-macro "AM_INIT_AUTOMAKE"))
962 (acf (autoconf-parameters-for-macro "AC_CONFIG_FILES"))
963 (aco (autoconf-parameters-for-macro "AC_OUTPUT"))
964 )
965 (cond
966 ;; AC init has more than 1 parameter
967 ((> (length aci) 1)
968 (setq name (nth 0 aci)
969 ver (nth 1 aci)
970 bugrep (nth 2 aci)))
971 ;; The init automake has more than 1 parameter
972 ((> (length aia) 1)
973 (setq name (nth 0 aia)
974 ver (nth 1 aia)
975 bugrep (nth 2 aia)))
976 )
977 ;; AC_CONFIG_FILES, or AC_OUTPUT lists everything that
978 ;; should be detected as part of this PROJECT, but not in a
979 ;; particular TARGET.
980 (let ((outfiles (cond (aco (list (car aco)))
981 (t acf))))
982 (if (> (length outfiles) 1)
983 (setq configfiles outfiles)
984 (setq configfiles (split-string (car outfiles) "\\s-" t)))
985 )
986 ))
987 )
988 ;; Else, try the script
989 ((file-exists-p conf-sh)
990 (project-am-with-config-current conf-sh
991 (setq name (project-am-extract-shell-variable "PACKAGE_NAME")
992 ver (project-am-extract-shell-variable "PACKAGE_VERSION")
993 )
994 ))
995 ;; Don't know what else....
996 (t
997 nil))
998 ;; Return stuff
999 (list name ver bugrep configfiles)
1000 ))
1001
1002 (defun project-am-package-info (dir)
1003 "Get the package information for directory topmost project dir over DIR.
1004 Calculates the info with `project-am-extract-package-info'."
1005 (let ((top (ede-toplevel)))
1006 (when top (setq dir (oref top :directory)))
1007 (project-am-extract-package-info dir)))
1008
1009 ;; for simple per project include path extension
1010 (defmethod ede-system-include-path ((this project-am-makefile))
1011 "Return `project-am-localvars-include-path', usually local variable
1012 per file or in .dir-locals.el or similar."
1013 (bound-and-true-p project-am-localvars-include-path))
1014
1015 (defmethod ede-system-include-path ((this project-am-target))
1016 "Return `project-am-localvars-include-path', usually local variable
1017 per file or in .dir-locals.el or similar."
1018 (bound-and-true-p project-am-localvars-include-path))
1019
1020
1021 (provide 'ede/project-am)
1022
1023 ;;; ede/project-am.el ends here