Fix typos.
[bpt/emacs.git] / lisp / cedet / ede / project-am.el
CommitLineData
acc33231
CY
1;;; project-am.el --- A project management scheme based on automake files.
2
73b0cd50 3;; Copyright (C) 1998-2000, 2003, 2005, 2007-2011
a785b776 4;; Free Software Foundation, Inc.
acc33231
CY
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
cb85c0d8 33;; the Automake file as its base template, and then maintains that
acc33231
CY
34;; information during edits, automatically updating the automake file
35;; where appropriate.
36
acc33231
CY
37(require 'make-mode)
38(require 'ede)
39(require 'ede/make)
40(require 'ede/makefile-edit)
cb85c0d8
EL
41(require 'semantic/find) ;; for semantic-find-tags-by-...
42(require 'ede/autoconf-edit)
acc33231
CY
43
44(declare-function autoconf-parameters-for-macro "ede/autoconf-edit")
cb85c0d8 45(declare-function ede-shell-run-something "ede/shell")
acc33231
CY
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.
cb85c0d8 99Each entry is of the form:
a785b776 100 (EMACSNAME CLASS AUTOMAKEVAR INDIRECT)
acc33231
CY
101where EMACSNAME is a name for Emacs to use.
102CLASS is the EDE target class to represent the target.
103AUTOMAKEVAR is the Automake variable to identify. This cannot be a
104 regular expression.
105INDIRECT is optional. If it is non-nil, then the variable in
106question lists other variables that need to be looked up.")
107
cb85c0d8
EL
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)
119where CLASS is the EDE target class for target.
120REGEXPVAR is the regexp used in `semantic-find-tags-by-name-regexp'.
121INDIRECT is optional. If it is non-nil, then the variable in it have
122other meta-variable based on this name.")
123
124
acc33231
CY
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.
199OT is the object target. DIR is the directory to start in."
a785b776 200 (let* ((target (if ede-object (error "Already associated w/ a target")
acc33231
CY
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))
acc33231
CY
217 (makefile-end-of-command)
218 (insert " " ofn)
219 (makefile-fill-paragraph nil)
220 (project-rescan ot)
221 (save-buffer))
222 (setq ede-object ot)))
223
224(defmethod project-remove-file ((ot project-am-target) fnnd)
225 "Remove the current buffer from any project targets."
226 (ede-with-projectfile ot
227 (makefile-move-to-macro (project-am-macro ot))
acc33231
CY
228 (makefile-navigate-macro (concat " *" (regexp-quote (ede-name fnnd))))
229 (replace-match "" t t nil 0)
230 (makefile-fill-paragraph nil)
231 (project-rescan ot)
232 (save-buffer))
233 (setq ede-object nil))
234
235(defmethod project-edit-file-target ((obj project-am-target))
236 "Edit the target associated w/ this file."
237 (find-file (concat (oref obj path) "Makefile.am"))
238 (goto-char (point-min))
239 (makefile-move-to-macro (project-am-macro obj))
240 (if (= (point-min) (point))
241 (re-search-forward (ede-target-name obj))))
242
243(defmethod project-new-target ((proj project-am-makefile)
244 &optional name type)
245 "Create a new target named NAME.
246Argument TYPE is the type of target to insert. This is a string
247matching something in `project-am-type-alist' or type class symbol.
248Despite the fact that this is a method, it depends on the current
249buffer being in order to provide a smart default target type."
250 (let* ((name (or name (read-string "Name: " "")))
251 (type (or type
252 (completing-read "Type: "
253 project-am-type-alist
254 nil t
255 (cond ((eq major-mode 'texinfo-mode)
256 "texinfo")
257 ((eq major-mode 'nroff-mode)
258 "man")
259 ((eq major-mode 'emacs-lisp-mode)
260 "lisp")
261 (t "bin")))))
262 (ntype (assoc type project-am-type-alist))
263 (ot nil))
264 (setq ot (apply (car (cdr ntype)) name :name name
265 :path (expand-file-name default-directory) nil))
266 (if (not ot) (error "Error creating target object %S" ntype))
267 (ede-with-projectfile ot
268 (goto-char (point-min))
acc33231
CY
269 (makefile-next-dependency)
270 (if (= (point) (point-min))
271 (goto-char (point-max))
272 (beginning-of-line)
273 (insert "\n")
274 (forward-char -1))
275 ;; Add the new target sources macro (if needed)
276 (if (project-am-macro ot)
277 (makefile-insert-macro (project-am-macro ot)))
278 ;; Add to the list of objects.
279 (goto-char (point-min))
280 (makefile-move-to-macro (car (cdr (cdr ntype))))
281 (if (= (point) (point-min))
282 (progn
283 (if (re-search-forward makefile-macroassign-regex nil t)
284 (progn (forward-line -1)
285 (end-of-line)
286 (insert "\n"))
287 ;; If the above search fails, thats ok. We'd just want to be at
288 ;; point-min anyway.
289 )
290 (makefile-insert-macro (car (cdr (cdr ntype))))))
291 (makefile-end-of-command)
292 (insert " " (ede-target-name ot))
293 (save-buffer)
294 ;; Rescan the object in this makefile.
295 (project-rescan ede-object))))
296
acc33231
CY
297;;
298;; NOTE TO SELF
299;;
300;; This should be handled at the EDE level, calling a method of the
301;; top most project.
302;;
303(defmethod project-compile-project ((obj project-am-target) &optional command)
304 "Compile the entire current project.
305Argument COMMAND is the command to use when compiling."
306 (require 'compile)
307 (if (not command)
308 (setq
309 command
310 ;; This interactive statement was taken from compile, and I'll
311 ;; use the same command history too.
312 (progn
313 (if (not project-am-compile-project-command)
314 (setq project-am-compile-project-command compile-command))
315 (if (or compilation-read-command current-prefix-arg)
316 (read-from-minibuffer "Project compile command: "
317 ;; hardcode make -k
318 ;; This is compile project after all.
319 project-am-compile-project-command
320 nil nil '(compile-history . 1))
321 project-am-compile-project-command))))
322 ;; When compile a project, we might be in a subdirectory,
323 ;; so we have to make sure we move all the way to the top.
324 (let* ((default-directory (project-am-find-topmost-level default-directory)))
325 (compile command)))
326
327(defmethod project-compile-project ((obj project-am-makefile)
328 &optional command)
329 "Compile the entire current project.
330Argument COMMAND is the command to use when compiling."
331 (require 'compile)
332 (if (not command)
333 (setq
334 command
335 ;; This interactive statement was taken from compile, and I'll
336 ;; use the same command history too.
337 (progn
338 (if (not project-am-compile-project-command)
339 (setq project-am-compile-project-command compile-command))
340 (if (or compilation-read-command current-prefix-arg)
341 (read-from-minibuffer "Project compile command: "
342 ;; hardcode make -k
343 ;; This is compile project after all.
344 project-am-compile-project-command
345 nil nil '(compile-history . 1))
346 project-am-compile-project-command))))
347 ;; When compile a project, we might be in a subdirectory,
348 ;; so we have to make sure we move all the way to the top.
349 (let* ((default-directory (project-am-find-topmost-level default-directory)))
350 (compile command)))
351
352(defmethod project-compile-target ((obj project-am-target) &optional command)
353 "Compile the current target.
354Argument COMMAND is the command to use for compiling the target."
355 (require 'compile)
356 (if (not project-am-compile-project-command)
357 (setq project-am-compile-project-command compile-command))
358 (if (not command)
359 (setq
360 command
361 (if compilation-read-command
362 (read-from-minibuffer "Project compile command: "
363 ;; hardcode make -k
364 ;; This is compile project after all.
365 (if ede-object
366 (format
367 project-am-compile-target-command
368 (project-compile-target-command
369 ede-object))
370 project-am-compile-target-command)
371 nil nil
372 '(compile-history . 1))
373 (if ede-object
374 project-am-compile-project-command
375 (format
376 project-am-compile-target-command
377 (project-compile-target-command ede-object))))))
378 ;; We better be in the right place when compiling a specific target.
379 (compile command))
380
381(defmethod project-debug-target ((obj project-am-objectcode))
382 "Run the current project target in a debugger."
383 (let ((tb (get-buffer-create " *padt*"))
384 (dd (oref obj path))
385 (cmd nil))
386 (unwind-protect
387 (progn
67d3ffe4 388 (require 'ede/shell)
acc33231
CY
389 (set-buffer tb)
390 (setq default-directory dd)
391 (setq cmd (read-from-minibuffer
392 "Run (like this): "
393 (concat (symbol-name project-am-debug-target-function)
394 " " (ede-target-name obj))))
395 (funcall project-am-debug-target-function cmd))
396 (kill-buffer tb))))
397
7bbe8d5a
GM
398(declare-function ede-shell-run-something "ede/shell")
399
67d3ffe4
CY
400(defmethod project-run-target ((obj project-am-objectcode))
401 "Run the current project target in comint buffer."
cb85c0d8 402 (require 'ede/shell)
67d3ffe4
CY
403 (let ((tb (get-buffer-create " *padt*"))
404 (dd (oref obj path))
405 (cmd nil))
406 (unwind-protect
407 (progn
408 (set-buffer tb)
409 (setq default-directory dd)
410 (setq cmd (read-from-minibuffer
411 "Run (like this): "
412 (concat (ede-target-name obj))))
413 (ede-shell-run-something obj cmd))
414 (kill-buffer tb))))
415
acc33231
CY
416(defmethod project-make-dist ((this project-am-target))
417 "Run the current project in the debugger."
418 (require 'compile)
419 (if (not project-am-compile-project-command)
420 (setq project-am-compile-project-command compile-command))
421 (project-compile-project this (concat project-am-compile-project-command
422 " dist")))
423
424;;; Project loading and saving
425;;
cb85c0d8
EL
426(defun project-am-load (directory &optional rootproj)
427 "Read an automakefile DIRECTORY into our data structure.
acc33231
CY
428If a given set of projects has already been loaded, then do nothing
429but return the project for the directory given.
430Optional ROOTPROJ is the root EDE project."
da6062e6 431 (let* ((ede-constructing t)
cb85c0d8
EL
432 (amo (object-assoc (expand-file-name "Makefile.am" directory)
433 'file ede-projects)))
434 (when (not amo)
435 (setq amo (project-am-load-makefile directory)))
436 amo))
acc33231
CY
437
438(defun project-am-find-topmost-level (dir)
439 "Find the topmost automakefile starting with DIR."
440 (let ((newdir dir))
441 (while (or (file-exists-p (concat newdir "Makefile.am"))
442 (file-exists-p (concat newdir "configure.ac"))
443 (file-exists-p (concat newdir "configure.in"))
444 )
445 (setq dir newdir newdir
446 (file-name-directory (directory-file-name newdir))))
447 (expand-file-name dir)))
448
449(defmacro project-am-with-makefile-current (dir &rest forms)
450 "Set the Makefile.am in DIR to be the current buffer.
451Run FORMS while the makefile is current.
452Kill the makefile if it was not loaded before the load."
453 `(let* ((fn (expand-file-name "Makefile.am" ,dir))
454 (fb nil)
455 (kb (get-file-buffer fn)))
456 (if (not (file-exists-p fn))
cb85c0d8
EL
457 nil
458 (save-excursion
459 (if kb (setq fb kb)
460 ;; We need to find-file this thing, but don't use
461 ;; any semantic features.
462 (let ((semantic-init-hook nil)
463 (recentf-exclude '( (lambda (f) t) ))
464 )
465 (setq fb (find-file-noselect fn)))
466 )
467 (set-buffer fb)
468 (prog1 ,@forms
469 (if (not kb) (kill-buffer (current-buffer))))))))
acc33231
CY
470(put 'project-am-with-makefile-current 'lisp-indent-function 1)
471
472(add-hook 'edebug-setup-hook
473 (lambda ()
474 (def-edebug-spec project-am-with-makefile-current
475 (form def-body))))
476
477
cb85c0d8 478(defun project-am-load-makefile (path &optional suggestedname)
acc33231 479 "Convert PATH into a project Makefile, and return its project object.
cb85c0d8
EL
480It does not check for existing project objects. Use `project-am-load'.
481Optional argument SUGGESTEDNAME will be the project name.
482This is used when subprojects are made in named subdirectories."
acc33231
CY
483 (project-am-with-makefile-current path
484 (if (and ede-object (project-am-makefile-p ede-object))
485 ede-object
486 (let* ((pi (project-am-package-info path))
cb85c0d8
EL
487 (sfn (when suggestedname
488 (project-am-last-dir suggestedname)))
489 (pn (or sfn (nth 0 pi) (project-am-last-dir fn)))
acc33231
CY
490 (ver (or (nth 1 pi) "0.0"))
491 (bug (nth 2 pi))
492 (cof (nth 3 pi))
493 (ampf (project-am-makefile
494 pn :name pn
495 :version ver
496 :mailinglist (or bug "")
497 :file fn)))
498 (oset ampf :directory (file-name-directory fn))
499 (oset ampf configureoutputfiles cof)
500 (make-local-variable 'ede-object)
501 (setq ede-object ampf)
502 ;; Move the rescan after we set ede-object to prevent recursion
503 (project-rescan ampf)
504 ampf))))
505
506;;; Methods:
acc33231
CY
507(defmethod project-targets-for-file ((proj project-am-makefile))
508 "Return a list of targets the project PROJ."
509 (oref proj targets))
510
511(defun project-am-scan-for-targets (currproj dir)
512 "Scan the current Makefile.am for targets.
513CURRPROJ is the current project being scanned.
514DIR is the directory to apply to new targets."
515 (let* ((otargets (oref currproj targets))
cb85c0d8
EL
516 ;; `ntargets' results in complete targets list
517 ;; not only the new targets by diffing.
acc33231
CY
518 (ntargets nil)
519 (tmp nil)
520 )
cb85c0d8
EL
521
522 (mapc
523 ;; Map all the different types
524 (lambda (typecar)
525 (let ((macro (nth 2 typecar))
526 (class (nth 1 typecar))
527 (indirect (nth 3 typecar))
acc33231 528 )
cb85c0d8
EL
529 (if indirect
530 ;; Map all the found objects
531 (mapc (lambda (lstcar)
532 (setq tmp (object-assoc lstcar 'name otargets))
533 (when (not tmp)
534 (setq tmp (apply class lstcar :name lstcar
535 :path dir nil)))
536 (project-rescan tmp)
537 (setq ntargets (cons tmp ntargets)))
538 (makefile-macro-file-list macro))
539 ;; Non-indirect will have a target whos sources
540 ;; are actual files, not names of other targets.
541 (let ((files (makefile-macro-file-list macro)))
542 (when files
543 (setq tmp (object-assoc macro 'name otargets))
544 (when (not tmp)
545 (setq tmp (apply class macro :name macro
546 :path dir nil)))
547 (project-rescan tmp)
548 (setq ntargets (cons tmp ntargets))
549 ))
550 )
551 ))
552 project-am-type-alist)
553
554 ;; At now check variables for meta-target regexp
555 ;; We have to check ntargets to avoid useless rescan.
556 ;; Also we have check otargets to prevent duplication.
557 (mapc
558 (lambda (typecar)
559 (let ((class (nth 0 typecar))
560 (metaregex (nth 1 typecar))
561 (indirect (nth 2 typecar)))
562 (if indirect
563 ;; Map all the found objects
564 (mapc
565 (lambda (lstcar)
566 (unless (object-assoc lstcar 'name ntargets)
567 (or
568 (setq tmp (object-assoc lstcar 'name otargets))
569 (setq tmp (apply class lstcar :name lstcar
570 :path dir nil)))
571 (project-rescan tmp)
572 (setq ntargets (cons tmp ntargets))))
573 ;; build a target list to map over
574 (let (atargets)
575 (dolist (TAG
576 (semantic-find-tags-by-name-regexp
577 metaregex (semantic-find-tags-by-class
578 'variable (semantic-fetch-tags))))
579 ;; default-value have to be a list
580 (when (cadr (assoc ':default-value TAG))
581 (setq atargets
582 (append
583 (nreverse (cadr (assoc ':default-value TAG)))
584 atargets))))
585 (nreverse atargets)))
586
587 ;; else not indirect, TODO: FIX various direct meta type in a sane way.
588 (dolist (T (semantic-find-tags-by-name-regexp
589 metaregex (semantic-find-tags-by-class
590 'variable (semantic-fetch-tags))))
591 (unless (setq tmp (object-assoc (car T) 'name ntargets))
592 (or (setq tmp (object-assoc (car T) 'name otargets))
593 ;; we are really new
594 (setq tmp (apply class (car T) :name (car T)
595 :path dir nil)))
596 (project-rescan tmp)
597 (setq ntargets (cons tmp ntargets))))
598 )))
599 project-am-meta-type-alist)
600 ntargets))
601
602(defun project-am-expand-subdirlist (place subdirs)
603 "Store in PLACE the SUBDIRS expanded from variables.
604Strip out duplicates, and recurse on variables."
605 (mapc (lambda (sp)
606 (let ((var (makefile-extract-varname-from-text sp)))
607 (if var
608 ;; If it is a variable, expand that variable, and keep going.
609 (project-am-expand-subdirlist
610 place (makefile-macro-file-list var))
611 ;; Else, add SP in if it isn't a dup.
612 (if (member sp (symbol-value place))
613 nil ; don't do it twice.
614 (set place (cons sp (symbol-value place))) ;; add
615 ))))
616 subdirs)
617 )
acc33231 618
cb85c0d8 619(defmethod project-rescan ((this project-am-makefile) &optional suggestedname)
acc33231
CY
620 "Rescan the makefile for all targets and sub targets."
621 (project-am-with-makefile-current (file-name-directory (oref this file))
622 ;;(message "Scanning %s..." (oref this file))
623 (let* ((pi (project-am-package-info (oref this directory)))
624 (pn (nth 0 pi))
625 (pv (nth 1 pi))
626 (bug (nth 2 pi))
627 (cof (nth 3 pi))
628 (osubproj (oref this subproj))
cb85c0d8
EL
629 ;; 1/30/10 - We need to append these two lists together,
630 ;; then strip out duplicates. Expanding this list (via
631 ;; references to other variables should also strip out dups
632 (csubproj (append
acc33231
CY
633 (makefile-macro-file-list "DIST_SUBDIRS")
634 (makefile-macro-file-list "SUBDIRS")))
635 (csubprojexpanded nil)
636 (nsubproj nil)
637 ;; Targets are excluded here because they require
638 ;; special attention.
639 (dir (expand-file-name default-directory))
640 (tmp nil)
641 (ntargets (project-am-scan-for-targets this dir))
642 )
cb85c0d8
EL
643 (if suggestedname
644 (oset this name (project-am-last-dir suggestedname))
645 ;; Else, setup toplevel project info.
646 (and pn (string= (directory-file-name
647 (oref this directory))
648 (directory-file-name
649 (project-am-find-topmost-level
650 (oref this directory))))
651 (oset this name pn)
652 (and pv (oset this version pv))
653 (and bug (oset this mailinglist bug))
654 (oset this configureoutputfiles cof)))
acc33231
CY
655 ;; Now that we have this new list, chuck the old targets
656 ;; and replace it with the new list of targets I just created.
657 (oset this targets (nreverse ntargets))
658 ;; We still have a list of targets. For all buffers, make sure
659 ;; their object still exists!
acc33231 660 ;; FIGURE THIS OUT
cb85c0d8 661 (project-am-expand-subdirlist 'csubprojexpanded csubproj)
c7015153 662 ;; Ok, now let's look at all our sub-projects.
acc33231 663 (mapc (lambda (sp)
cb85c0d8
EL
664 (let* ((subdir (file-name-as-directory
665 (expand-file-name
666 sp (file-name-directory (oref this :file)))))
667 (submake (expand-file-name
668 "Makefile.am"
669 subdir)))
670 (if (string= submake (oref this :file))
671 nil ;; don't recurse.. please!
672 ;; For each project id found, see if we need to recycle,
673 ;; and if we do not, then make a new one. Check the deep
674 ;; rescan value for behavior patterns.
675 (setq tmp (object-assoc
676 submake
677 'file osubproj))
678 (if (not tmp)
679 (setq tmp
680 (condition-case nil
681 ;; In case of problem, ignore it.
682 (project-am-load-makefile subdir subdir)
683 (error nil)))
684 ;; If we have tmp, then rescan it only if deep mode.
685 (if ede-deep-rescan
686 (project-rescan tmp subdir)))
687 ;; Tac tmp onto our list of things to keep, but only
688 ;; if tmp was found.
689 (when tmp
690 ;;(message "Adding %S" (object-print tmp))
691 (setq nsubproj (cons tmp nsubproj)))))
692 )
693 (nreverse csubprojexpanded))
acc33231
CY
694 (oset this subproj nsubproj)
695 ;; All elements should be updated now.
696 )))
697
698
699(defmethod project-rescan ((this project-am-program))
700 "Rescan object THIS."
701 (oset this :source (makefile-macro-file-list (project-am-macro this)))
cb85c0d8
EL
702 (unless (oref this :source)
703 (oset this :source (list (concat (oref this :name) ".c"))))
acc33231
CY
704 (oset this :ldadd (makefile-macro-file-list
705 (concat (oref this :name) "_LDADD"))))
706
707(defmethod project-rescan ((this project-am-lib))
708 "Rescan object THIS."
cb85c0d8
EL
709 (oset this :source (makefile-macro-file-list (project-am-macro this)))
710 (unless (oref this :source)
711 (oset this :source (list (concat (file-name-sans-extension (oref this :name)) ".c")))))
acc33231
CY
712
713(defmethod project-rescan ((this project-am-texinfo))
714 "Rescan object THIS."
715 (oset this :include (makefile-macro-file-list (project-am-macro this))))
716
717(defmethod project-rescan ((this project-am-man))
718 "Rescan object THIS."
719 (oset this :source (makefile-macro-file-list (project-am-macro this))))
720
721(defmethod project-rescan ((this project-am-lisp))
722 "Rescan the lisp sources."
723 (oset this :source (makefile-macro-file-list (project-am-macro this))))
724
725(defmethod project-rescan ((this project-am-header))
726 "Rescan the Header sources for object THIS."
727 (oset this :source (makefile-macro-file-list (project-am-macro this))))
728
729(defmethod project-rescan ((this project-am-built-src))
730 "Rescan built sources for object THIS."
731 (oset this :source (makefile-macro-file-list "BUILT_SOURCES")))
732
733(defmethod project-rescan ((this project-am-extra-dist))
734 "Rescan object THIS."
735 (oset this :source (makefile-macro-file-list "EXTRA_DIST")))
acc33231
CY
736
737(defmethod project-am-macro ((this project-am-objectcode))
738 "Return the default macro to 'edit' for this object type."
739 (concat (subst-char-in-string ?- ?_ (oref this :name)) "_SOURCES"))
740
741(defmethod project-am-macro ((this project-am-header-noinst))
742 "Return the default macro to 'edit' for this object."
743 "noinst_HEADERS")
744
745(defmethod project-am-macro ((this project-am-header-inst))
746 "Return the default macro to 'edit' for this object."
747 "include_HEADERS")
748
749(defmethod project-am-macro ((this project-am-header-pkg))
750 "Return the default macro to 'edit' for this object."
751 "pkginclude_HEADERS")
752
753(defmethod project-am-macro ((this project-am-header-chk))
754 "Return the default macro to 'edit' for this object."
755 "check_HEADERS")
756
757(defmethod project-am-macro ((this project-am-texinfo))
758 "Return the default macro to 'edit' for this object type."
759 (concat (file-name-sans-extension (oref this :name)) "_TEXINFOS"))
760
761(defmethod project-am-macro ((this project-am-man))
762 "Return the default macro to 'edit' for this object type."
763 (oref this :name))
764
765(defmethod project-am-macro ((this project-am-lisp))
766 "Return the default macro to 'edit' for this object."
767 "lisp_LISP")
768
769(defun project-am-buffer-object (amf buffer)
770 "Return an object starting with AMF associated with BUFFER.
771nil means that this buffer belongs to no-one."
772 (if (not amf)
773 nil
774 (if (ede-buffer-mine amf buffer)
775 amf
776 (let ((targ (oref amf targets))
777 (sobj (oref amf subproj))
778 (obj nil))
779 (while (and targ (not obj))
780 (if (ede-buffer-mine (car targ) buffer)
781 (setq obj (car targ)))
782 (setq targ (cdr targ)))
783 (while (and sobj (not obj))
784 (setq obj (project-am-buffer-object (car sobj) buffer)
785 sobj (cdr sobj)))
786 obj))))
787
788(defmethod ede-buffer-mine ((this project-am-makefile) buffer)
789 "Return t if object THIS lays claim to the file in BUFFER."
790 (let ((efn (expand-file-name (buffer-file-name buffer))))
791 (or (string= (oref this :file) efn)
792 (string-match "/configure\\.ac$" efn)
793 (string-match "/configure\\.in$" efn)
794 (string-match "/configure$" efn)
795 ;; Search output files.
796 (let ((ans nil))
797 (dolist (f (oref this configureoutputfiles))
798 (when (string-match (concat (regexp-quote f) "$") efn)
799 (setq ans t)))
800 ans)
801 )))
802
803(defmethod ede-buffer-mine ((this project-am-objectcode) buffer)
804 "Return t if object THIS lays claim to the file in BUFFER."
cb85c0d8 805 (member (file-relative-name (buffer-file-name buffer) (oref this :path))
acc33231
CY
806 (oref this :source)))
807
808(defmethod ede-buffer-mine ((this project-am-texinfo) buffer)
809 "Return t if object THIS lays claim to the file in BUFFER."
cb85c0d8
EL
810 (let ((bfn (file-relative-name (buffer-file-name buffer)
811 (oref this :path))))
812 (or (string= (oref this :name) bfn)
813 (member bfn (oref this :include)))))
acc33231
CY
814
815(defmethod ede-buffer-mine ((this project-am-man) buffer)
816 "Return t if object THIS lays claim to the file in BUFFER."
cb85c0d8
EL
817 (string= (oref this :name)
818 (file-relative-name (buffer-file-name buffer) (oref this :path))))
acc33231
CY
819
820(defmethod ede-buffer-mine ((this project-am-lisp) buffer)
821 "Return t if object THIS lays claim to the file in BUFFER."
cb85c0d8 822 (member (file-relative-name (buffer-file-name buffer) (oref this :path))
acc33231
CY
823 (oref this :source)))
824
825(defmethod project-am-subtree ((ampf project-am-makefile) subdir)
826 "Return the sub project in AMPF specified by SUBDIR."
827 (object-assoc (expand-file-name subdir) 'file (oref ampf subproj)))
828
829(defmethod project-compile-target-command ((this project-am-target))
830 "Default target to use when compiling a given target."
831 ;; This is a pretty good default for most.
832 "")
833
834(defmethod project-compile-target-command ((this project-am-objectcode))
835 "Default target to use when compiling an object code target."
836 (oref this :name))
837
838(defmethod project-compile-target-command ((this project-am-texinfo))
0d26e0b6 839 "Default target t- use when compiling a texinfo file."
acc33231
CY
840 (let ((n (oref this :name)))
841 (if (string-match "\\.texi?\\(nfo\\)?" n)
842 (setq n (replace-match ".info" t t n)))
843 n))
844
845\f
846;;; Generic useful functions
847
848(defun project-am-last-dir (file)
849 "Return the last part of a directory name.
850Argument FILE is the file to extract the end directory name from."
851 (let* ((s (file-name-directory file))
852 (d (directory-file-name s))
853 )
854 (file-name-nondirectory d))
855 )
856
857(defun project-am-preferred-target-type (file)
858 "For FILE, return the preferred type for that file."
859 (cond ((string-match "\\.texi?\\(nfo\\)$" file)
860 project-am-texinfo)
861 ((string-match "\\.[0-9]$" file)
862 project-am-man)
863 ((string-match "\\.el$" file)
864 project-am-lisp)
865 (t
866 project-am-program)))
867
868(defmethod ede-buffer-header-file((this project-am-objectcode) buffer)
869 "There are no default header files."
870 (or (call-next-method)
871 (let ((s (oref this source))
872 (found nil))
873 (while (and s (not found))
874 ;; Add more logic here if applicable.
875 (if (string-match "\\.\\(h\\|H\\|hh\\|hpp\\)" (car s))
876 (setq found (car s)))
877 (setq s (cdr s)))
878 found)))
879
880(defmethod ede-documentation ((this project-am-texinfo))
881 "Return a list of files that provides documentation.
882Documentation is not for object THIS, but is provided by THIS for other
883files in the project."
884 (let* ((src (append (oref this source)
885 (oref this include)))
886 (proj (ede-target-parent this))
887 (dir (oref proj directory))
888 (out nil))
889 ;; Loop over all entries and expand
890 (while src
891 (setq out (cons
892 (expand-file-name (car src) dir)
893 out))
894 (setq src (cdr src)))
895 ;; return it
896 out))
897
898
899;;; Configure.in queries.
900;;
901(defvar project-am-autoconf-file-options
902 '("configure.in" "configure.ac")
903 "List of possible configure files to look in for project info.")
904
905(defun project-am-autoconf-file (dir)
906 "Return the name of the autoconf file to use in DIR."
907 (let ((ans nil))
908 (dolist (L project-am-autoconf-file-options)
909 (when (file-exists-p (expand-file-name L dir))
910 (setq ans (expand-file-name L dir))))
911 ans))
912
913(defmacro project-am-with-config-current (file &rest forms)
914 "Set the Configure FILE in the top most directory above DIR as current.
915Run FORMS in the configure file.
916Kill the Configure buffer if it was not already in a buffer."
917 `(save-excursion
918 (let ((fb (generate-new-buffer ,file)))
919 (set-buffer fb)
920 (erase-buffer)
921 (insert-file-contents ,file)
922 (prog1 ,@forms
923 (kill-buffer fb)))))
924
925(put 'project-am-with-config-current 'lisp-indent-function 1)
926
927(add-hook 'edebug-setup-hook
928 (lambda ()
929 (def-edebug-spec project-am-with-config-current
930 (form def-body))))
931
932(defmacro project-am-extract-shell-variable (var)
933 "Extract the value of the shell variable VAR from a shell script."
934 (save-excursion
935 (goto-char (point-min))
936 (when (re-search-forward (concat "^" (regexp-quote var) "\\s-*=\\s-*")
937 nil t)
938 (buffer-substring-no-properties (point) (point-at-eol)))))
939
940(defun project-am-extract-package-info (dir)
941 "Extract the package information for directory DIR."
942 (let ((conf-in (project-am-autoconf-file dir))
943 (conf-sh (expand-file-name "configure" dir))
944 (name (file-name-nondirectory
945 (directory-file-name dir)))
946 (ver "1.0")
947 (bugrep nil)
948 (configfiles nil)
949 )
950 (cond
951 ;; Try configure.in or configure.ac
952 (conf-in
acc33231
CY
953 (project-am-with-config-current conf-in
954 (let ((aci (autoconf-parameters-for-macro "AC_INIT"))
955 (aia (autoconf-parameters-for-macro "AM_INIT_AUTOMAKE"))
956 (acf (autoconf-parameters-for-macro "AC_CONFIG_FILES"))
957 (aco (autoconf-parameters-for-macro "AC_OUTPUT"))
958 )
959 (cond
960 ;; AC init has more than 1 parameter
961 ((> (length aci) 1)
962 (setq name (nth 0 aci)
963 ver (nth 1 aci)
964 bugrep (nth 2 aci)))
965 ;; The init automake has more than 1 parameter
966 ((> (length aia) 1)
967 (setq name (nth 0 aia)
968 ver (nth 1 aia)
969 bugrep (nth 2 aia)))
970 )
971 ;; AC_CONFIG_FILES, or AC_OUTPUT lists everything that
972 ;; should be detected as part of this PROJECT, but not in a
973 ;; particular TARGET.
974 (let ((outfiles (cond (aco (list (car aco)))
975 (t acf))))
976 (if (> (length outfiles) 1)
977 (setq configfiles outfiles)
cb85c0d8 978 (setq configfiles (split-string (car outfiles) "\\s-" t)))
acc33231
CY
979 )
980 ))
981 )
982 ;; Else, try the script
983 ((file-exists-p conf-sh)
984 (project-am-with-config-current conf-sh
985 (setq name (project-am-extract-shell-variable "PACKAGE_NAME")
986 ver (project-am-extract-shell-variable "PACKAGE_VERSION")
987 )
988 ))
989 ;; Don't know what else....
990 (t
991 nil))
992 ;; Return stuff
993 (list name ver bugrep configfiles)
994 ))
995
996(defun project-am-package-info (dir)
997 "Get the package information for directory topmost project dir over DIR.
a785b776 998Calculates the info with `project-am-extract-package-info'."
acc33231
CY
999 (let ((top (ede-toplevel)))
1000 (when top (setq dir (oref top :directory)))
1001 (project-am-extract-package-info dir)))
1002
cb85c0d8
EL
1003;; for simple per project include path extension
1004(defmethod ede-system-include-path ((this project-am-makefile))
1005 "Return `project-am-localvars-include-path', usually local variable
1006per file or in .dir-locals.el or similar."
1007 (bound-and-true-p project-am-localvars-include-path))
1008
1009(defmethod ede-system-include-path ((this project-am-target))
1010 "Return `project-am-localvars-include-path', usually local variable
1011per file or in .dir-locals.el or similar."
1012 (bound-and-true-p project-am-localvars-include-path))
1013
1014
acc33231
CY
1015(provide 'ede/project-am)
1016
1017;;; ede/project-am.el ends here