1 ;;; project-am.el --- A project management scheme based on automake files.
3 ;; Copyright (C) 1998-2000, 2003, 2005, 2007-2018 Free Software
6 ;; Author: Eric M. Ludlam <zappo@gnu.org>
8 ;; Keywords: project, make
10 ;; This file is part of GNU Emacs.
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.
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.
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
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
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
40 (require 'ede
/makefile-edit
)
41 (require 'semantic
/find
) ;; for semantic-find-tags-by-...
42 (require 'ede
/autoconf-edit
)
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
))
49 (defgroup project-am nil
50 "File and tag browser frame."
55 (defcustom project-am-compile-project-command nil
56 "Default command used to compile a project."
58 :type
'(choice (const nil
) string
))
60 (defcustom project-am-compile-target-command
(concat ede-make-command
" -k %s")
61 "Default command used to compile a project."
65 (defcustom project-am-debug-target-function
'gdb
66 "Default Emacs command used to debug a target."
68 :type
'function
) ; make this be a list some day
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)
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
105 INDIRECT is optional. If it is non-nil, then the variable in
106 question lists other variables that need to be looked up.")
109 (defconst project-am-meta-type-alist
110 '((project-am-program "_PROGRAMS$" t
)
111 (project-am-lib "_\\(LIBS\\|LIBRARIES\\|LTLIBRARIES\\)$" t
)
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
)
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.")
125 (defclass project-am-target
(ede-target)
127 "Base target class for everything in project-am.")
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.")
133 (defclass project-am-program
(project-am-objectcode)
134 ((ldadd :initarg
:ldadd
:documentation
"Additional LD args."
136 "A top level program to build")
138 (defclass project-am-header
(project-am-target)
140 "A group of misc source files, such as headers.")
142 (defclass project-am-header-noinst
(project-am-header)
144 "A group of header files that are not installed.")
146 (defclass project-am-header-inst
(project-am-header)
148 "A group of header files that are not installed.")
150 (defclass project-am-header-pkg
(project-am-header)
152 "A group of header files that are not installed.")
154 (defclass project-am-header-chk
(project-am-header)
156 "A group of header files that are not installed.")
158 (defclass project-am-lib
(project-am-objectcode)
160 "A top level library to build")
162 (defclass project-am-lisp
(project-am-target)
164 "A group of Emacs Lisp programs to byte compile.")
166 (defclass project-am-texinfo
(project-am-target)
167 ((include :initarg
:include
169 :documentation
"Additional texinfo included in this one."))
170 "A top level texinfo file to build.")
172 (defclass project-am-man
(project-am-target)
174 "A top level man file to build.")
176 ;; For generic files tracker like EXTRA_DIST
177 (defclass project-am-built-src
(project-am-target)
179 "A group of Emacs Lisp programs to byte compile.")
181 (defclass project-am-extra-dist
(project-am-target)
183 "A group of Emacs Lisp programs to byte compile.")
185 (defclass project-am-makefile
(ede-project)
186 ((targets :initarg
:targets
188 :documentation
"Top level targets in this makefile.")
189 (configureoutputfiles
192 "List of files output from configure system.")
194 "Encode one makefile.")
197 (cl-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
207 ;; The input target might be new. See if we can find it.
208 (amf (ede-target-parent ot
))
209 (ot (object-assoc target
'name
(oref amf targets
)))
210 (ofn (file-name-nondirectory (buffer-file-name))))
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 (makefile-end-of-command)
219 (makefile-fill-paragraph nil
)
222 (setq ede-object ot
)))
224 (cl-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
))
228 (makefile-navigate-macro (concat " *" (regexp-quote (ede-name fnnd
))))
229 (replace-match "" t t nil
0)
230 (makefile-fill-paragraph nil
)
233 (setq ede-object nil
))
235 (cl-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
))))
243 (cl-defmethod project-new-target ((proj project-am-makefile
)
245 "Create a new target named NAME.
246 Argument TYPE is the type of target to insert. This is a string
247 matching something in `project-am-type-alist' or type class symbol.
248 Despite the fact that this is a method, it depends on the current
249 buffer being in order to provide a smart default target type."
250 (let* ((name (or name
(read-string "Name: " "")))
252 (completing-read "Type: "
253 project-am-type-alist
255 (cond ((eq major-mode
'texinfo-mode
)
257 ((eq major-mode
'nroff-mode
)
259 ((eq major-mode
'emacs-lisp-mode
)
262 (ntype (assoc type project-am-type-alist
))
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))
269 (makefile-next-dependency)
270 (if (= (point) (point-min))
271 (goto-char (point-max))
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))
283 (if (re-search-forward makefile-macroassign-regex nil t
)
284 (progn (forward-line -
1)
287 ;; If the above search fails, that's ok. We'd just want to be at
290 (makefile-insert-macro (car (cdr (cdr ntype
))))))
291 (makefile-end-of-command)
292 (insert " " (ede-target-name ot
))
294 ;; Rescan the object in this makefile.
295 (project-rescan ede-object
))))
300 ;; This should be handled at the EDE level, calling a method of the
303 (cl-defmethod project-compile-project ((obj project-am-target
) &optional command
)
304 "Compile the entire current project.
305 Argument COMMAND is the command to use when compiling."
310 ;; This interactive statement was taken from compile, and I'll
311 ;; use the same command history too.
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: "
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
)))
327 (cl-defmethod project-compile-project ((obj project-am-makefile
)
329 "Compile the entire current project.
330 Argument COMMAND is the command to use when compiling."
335 ;; This interactive statement was taken from compile, and I'll
336 ;; use the same command history too.
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: "
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
)))
352 (cl-defmethod project-compile-target ((obj project-am-target
) &optional command
)
353 "Compile the current target.
354 Argument COMMAND is the command to use for compiling the target."
356 (if (not project-am-compile-project-command
)
357 (setq project-am-compile-project-command compile-command
))
361 (if compilation-read-command
362 (read-from-minibuffer "Project compile command: "
364 ;; This is compile project after all.
367 project-am-compile-target-command
368 (project-compile-target-command
370 project-am-compile-target-command
)
372 '(compile-history .
1))
374 project-am-compile-project-command
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.
381 (cl-defmethod project-debug-target ((obj project-am-objectcode
))
382 "Run the current project target in a debugger."
383 (let ((tb (get-buffer-create " *padt*"))
390 (setq default-directory dd
)
391 (setq cmd
(read-from-minibuffer
393 (concat (symbol-name project-am-debug-target-function
)
394 " " (ede-target-name obj
))))
395 (funcall project-am-debug-target-function cmd
))
398 (declare-function ede-shell-run-something
"ede/shell")
400 (cl-defmethod project-run-target ((obj project-am-objectcode
))
401 "Run the current project target in comint buffer."
403 (let ((tb (get-buffer-create " *padt*"))
409 (setq default-directory dd
)
410 (setq cmd
(read-from-minibuffer
412 (concat "./" (ede-target-name obj
))))
413 (ede-shell-run-something obj cmd
))
416 (cl-defmethod project-make-dist ((this project-am-target
))
417 "Run the current project in the debugger."
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
424 ;;; Project loading and saving
426 (defun project-am-load (directory &optional rootproj
)
427 "Read an automakefile DIRECTORY into our data structure.
428 If a given set of projects has already been loaded, then do nothing
429 but return the project for the directory given.
430 Optional ROOTPROJ is the root EDE project."
431 ;; Just jump into creating the project from the Makefiles.
432 (project-am-load-makefile directory
))
434 (defun project-am-find-topmost-level (dir)
435 "Find the topmost automakefile starting with DIR."
437 (while (or (file-exists-p (concat newdir
"Makefile.am"))
438 (file-exists-p (concat newdir
"configure.ac"))
439 (file-exists-p (concat newdir
"configure.in"))
441 (setq dir newdir newdir
442 (file-name-directory (directory-file-name newdir
))))
443 (expand-file-name dir
)))
445 (defmacro project-am-with-makefile-current
(dir &rest forms
)
446 "Set the Makefile.am in DIR to be the current buffer.
447 Run FORMS while the makefile is current.
448 Kill the makefile if it was not loaded before the load."
449 `(let* ((fn (expand-file-name "Makefile.am" ,dir
))
451 (kb (get-file-buffer fn
)))
452 (if (not (file-exists-p fn
))
456 ;; We need to find-file this thing, but don't use
457 ;; any semantic features.
458 (let ((semantic-init-hook nil
)
459 (recentf-exclude '( (lambda (f) t
) ))
461 (setq fb
(find-file-noselect fn
)))
465 (if (not kb
) (kill-buffer (current-buffer))))))))
466 (put 'project-am-with-makefile-current
'lisp-indent-function
1)
468 (add-hook 'edebug-setup-hook
470 (def-edebug-spec project-am-with-makefile-current
474 (defun project-am-load-makefile (path &optional suggestedname
)
475 "Convert PATH into a project Makefile, and return its project object.
476 It does not check for existing project objects. Use `project-am-load'.
477 Optional argument SUGGESTEDNAME will be the project name.
478 This is used when subprojects are made in named subdirectories."
479 (project-am-with-makefile-current path
480 (if (and ede-object
(project-am-makefile-p ede-object
))
482 (let* ((pi (project-am-package-info path
))
483 (sfn (when suggestedname
484 (project-am-last-dir suggestedname
)))
485 (pn (or sfn
(nth 0 pi
) (project-am-last-dir fn
)))
486 (ver (or (nth 1 pi
) "0.0"))
489 (ampf (project-am-makefile
492 :mailinglist
(or bug
"")
494 (oset ampf
:directory
(file-name-directory fn
))
495 (oset ampf configureoutputfiles cof
)
496 (make-local-variable 'ede-object
)
497 (setq ede-object ampf
)
498 ;; Move the rescan after we set ede-object to prevent recursion
499 (project-rescan ampf
)
503 (cl-defmethod project-targets-for-file ((proj project-am-makefile
))
504 "Return a list of targets the project PROJ."
507 (defun project-am-scan-for-targets (currproj dir
)
508 "Scan the current Makefile.am for targets.
509 CURRPROJ is the current project being scanned.
510 DIR is the directory to apply to new targets."
511 (let* ((otargets (oref currproj targets
))
512 ;; `ntargets' results in complete targets list
513 ;; not only the new targets by diffing.
519 ;; Map all the different types
521 (let ((macro (nth 2 typecar
))
522 (class (nth 1 typecar
))
523 (indirect (nth 3 typecar
))
526 ;; Map all the found objects
527 (mapc (lambda (lstcar)
528 (setq tmp
(object-assoc lstcar
'name otargets
))
530 (setq tmp
(apply class lstcar
:name lstcar
533 (setq ntargets
(cons tmp ntargets
)))
534 (makefile-macro-file-list macro
))
535 ;; Non-indirect will have a target whos sources
536 ;; are actual files, not names of other targets.
537 (let ((files (makefile-macro-file-list macro
)))
539 (setq tmp
(object-assoc macro
'name otargets
))
541 (setq tmp
(apply class macro
:name macro
544 (setq ntargets
(cons tmp ntargets
))
548 project-am-type-alist
)
550 ;; At now check variables for meta-target regexp
551 ;; We have to check ntargets to avoid useless rescan.
552 ;; Also we have check otargets to prevent duplication.
555 (let ((class (nth 0 typecar
))
556 (metaregex (nth 1 typecar
))
557 (indirect (nth 2 typecar
)))
559 ;; Map all the found objects
562 (unless (object-assoc lstcar
'name ntargets
)
564 (setq tmp
(object-assoc lstcar
'name otargets
))
565 (setq tmp
(apply class lstcar
:name lstcar
568 (setq ntargets
(cons tmp ntargets
))))
569 ;; build a target list to map over
572 (semantic-find-tags-by-name-regexp
573 metaregex
(semantic-find-tags-by-class
574 'variable
(semantic-fetch-tags))))
575 ;; default-value have to be a list
576 (when (cadr (assoc ':default-value TAG
))
579 (nreverse (cadr (assoc ':default-value TAG
)))
581 (nreverse atargets
)))
583 ;; else not indirect, TODO: FIX various direct meta type in a sane way.
584 (dolist (T (semantic-find-tags-by-name-regexp
585 metaregex
(semantic-find-tags-by-class
586 'variable
(semantic-fetch-tags))))
587 (unless (setq tmp
(object-assoc (car T
) 'name ntargets
))
588 (or (setq tmp
(object-assoc (car T
) 'name otargets
))
590 (setq tmp
(apply class
(car T
) :name
(car T
)
593 (setq ntargets
(cons tmp ntargets
))))
595 project-am-meta-type-alist
)
598 (defun project-am-expand-subdirlist (place subdirs
)
599 "Store in PLACE the SUBDIRS expanded from variables.
600 Strip out duplicates, and recurse on variables."
602 (let ((var (makefile-extract-varname-from-text sp
)))
604 ;; If it is a variable, expand that variable, and keep going.
605 (project-am-expand-subdirlist
606 place
(makefile-macro-file-list var
))
607 ;; Else, add SP in if it isn't a dup.
608 (if (member sp
(symbol-value place
))
609 nil
; don't do it twice.
610 (set place
(cons sp
(symbol-value place
))) ;; add
615 (cl-defmethod project-rescan ((this project-am-makefile
) &optional suggestedname
)
616 "Rescan the makefile for all targets and sub targets."
617 (project-am-with-makefile-current (file-name-directory (oref this file
))
618 ;;(message "Scanning %s..." (oref this file))
619 (let* ((pi (project-am-package-info (oref this directory
)))
624 (osubproj (oref this subproj
))
625 ;; 1/30/10 - We need to append these two lists together,
626 ;; then strip out duplicates. Expanding this list (via
627 ;; references to other variables should also strip out dups
629 (makefile-macro-file-list "DIST_SUBDIRS")
630 (makefile-macro-file-list "SUBDIRS")))
631 (csubprojexpanded nil
)
633 ;; Targets are excluded here because they require
634 ;; special attention.
635 (dir (expand-file-name default-directory
))
637 (ntargets (project-am-scan-for-targets this dir
))
640 (oset this name
(project-am-last-dir suggestedname
))
641 ;; Else, setup toplevel project info.
642 (and pn
(string= (directory-file-name
643 (oref this directory
))
645 (project-am-find-topmost-level
646 (oref this directory
))))
648 (and pv
(oset this version pv
))
649 (and bug
(oset this mailinglist bug
))
650 (oset this configureoutputfiles cof
)))
651 ;; Now that we have this new list, chuck the old targets
652 ;; and replace it with the new list of targets I just created.
653 (oset this targets
(nreverse ntargets
))
654 ;; We still have a list of targets. For all buffers, make sure
655 ;; their object still exists!
657 (project-am-expand-subdirlist 'csubprojexpanded csubproj
)
658 ;; Ok, now let's look at all our sub-projects.
660 (let* ((subdir (file-name-as-directory
662 sp
(file-name-directory (oref this
:file
)))))
663 (submake (expand-file-name
666 (if (string= submake
(oref this
:file
))
667 nil
;; don't recurse.. please!
668 ;; For each project id found, see if we need to recycle,
669 ;; and if we do not, then make a new one. Check the deep
670 ;; rescan value for behavior patterns.
671 (setq tmp
(object-assoc
677 ;; In case of problem, ignore it.
678 (project-am-load-makefile subdir subdir
)
680 ;; If we have tmp, then rescan it only if deep mode.
682 (project-rescan tmp subdir
)))
683 ;; Tac tmp onto our list of things to keep, but only
686 ;;(message "Adding %S" (object-print tmp))
687 (setq nsubproj
(cons tmp nsubproj
)))))
689 (nreverse csubprojexpanded
))
690 (oset this subproj nsubproj
)
691 ;; All elements should be updated now.
695 (cl-defmethod project-rescan ((this project-am-program
))
696 "Rescan object THIS."
697 (oset this
:source
(makefile-macro-file-list (project-am-macro this
)))
698 (unless (oref this
:source
)
699 (oset this
:source
(list (concat (oref this
:name
) ".c"))))
700 (oset this
:ldadd
(makefile-macro-file-list
701 (concat (oref this
:name
) "_LDADD"))))
703 (cl-defmethod project-rescan ((this project-am-lib
))
704 "Rescan object THIS."
705 (oset this
:source
(makefile-macro-file-list (project-am-macro this
)))
706 (unless (oref this
:source
)
707 (oset this
:source
(list (concat (file-name-sans-extension (oref this
:name
)) ".c")))))
709 (cl-defmethod project-rescan ((this project-am-texinfo
))
710 "Rescan object THIS."
711 (oset this
:include
(makefile-macro-file-list (project-am-macro this
))))
713 (cl-defmethod project-rescan ((this project-am-man
))
714 "Rescan object THIS."
715 (oset this
:source
(makefile-macro-file-list (project-am-macro this
))))
717 (cl-defmethod project-rescan ((this project-am-lisp
))
718 "Rescan the lisp sources."
719 (oset this
:source
(makefile-macro-file-list (project-am-macro this
))))
721 (cl-defmethod project-rescan ((this project-am-header
))
722 "Rescan the Header sources for object THIS."
723 (oset this
:source
(makefile-macro-file-list (project-am-macro this
))))
725 (cl-defmethod project-rescan ((this project-am-built-src
))
726 "Rescan built sources for object THIS."
727 (oset this
:source
(makefile-macro-file-list "BUILT_SOURCES")))
729 (cl-defmethod project-rescan ((this project-am-extra-dist
))
730 "Rescan object THIS."
731 (oset this
:source
(makefile-macro-file-list "EXTRA_DIST")))
733 (cl-defmethod project-am-macro ((this project-am-objectcode
))
734 "Return the default macro to `edit' for this object type."
735 (concat (subst-char-in-string ?- ?_
(oref this
:name
)) "_SOURCES"))
737 (cl-defmethod project-am-macro ((this project-am-header-noinst
))
738 "Return the default macro to `edit' for this object."
741 (cl-defmethod project-am-macro ((this project-am-header-inst
))
742 "Return the default macro to `edit' for this object."
745 (cl-defmethod project-am-macro ((this project-am-header-pkg
))
746 "Return the default macro to `edit' for this object."
747 "pkginclude_HEADERS")
749 (cl-defmethod project-am-macro ((this project-am-header-chk
))
750 "Return the default macro to `edit' for this object."
753 (cl-defmethod project-am-macro ((this project-am-texinfo
))
754 "Return the default macro to `edit' for this object type."
755 (concat (file-name-sans-extension (oref this
:name
)) "_TEXINFOS"))
757 (cl-defmethod project-am-macro ((this project-am-man
))
758 "Return the default macro to `edit' for this object type."
761 (cl-defmethod project-am-macro ((this project-am-lisp
))
762 "Return the default macro to `edit' for this object."
765 (defun project-am-buffer-object (amf buffer
)
766 "Return an object starting with AMF associated with BUFFER.
767 nil means that this buffer belongs to no-one."
770 (if (ede-buffer-mine amf buffer
)
772 (let ((targ (oref amf targets
))
773 (sobj (oref amf subproj
))
775 (while (and targ
(not obj
))
776 (if (ede-buffer-mine (car targ
) buffer
)
777 (setq obj
(car targ
)))
778 (setq targ
(cdr targ
)))
779 (while (and sobj
(not obj
))
780 (setq obj
(project-am-buffer-object (car sobj
) buffer
)
784 (cl-defmethod ede-buffer-mine ((this project-am-makefile
) buffer
)
785 "Return t if object THIS lays claim to the file in BUFFER."
786 (let ((efn (expand-file-name (buffer-file-name buffer
))))
787 (or (string= (oref this
:file
) efn
)
788 (string-match "/configure\\.ac$" efn
)
789 (string-match "/configure\\.in$" efn
)
790 (string-match "/configure$" efn
)
791 ;; Search output files.
793 (dolist (f (oref this configureoutputfiles
))
794 (when (string-match (concat (regexp-quote f
) "$") efn
)
799 (cl-defmethod ede-buffer-mine ((this project-am-objectcode
) buffer
)
800 "Return t if object THIS lays claim to the file in BUFFER."
801 (member (file-relative-name (buffer-file-name buffer
) (oref this
:path
))
802 (oref this
:source
)))
804 (cl-defmethod ede-buffer-mine ((this project-am-texinfo
) buffer
)
805 "Return t if object THIS lays claim to the file in BUFFER."
806 (let ((bfn (file-relative-name (buffer-file-name buffer
)
808 (or (string= (oref this
:name
) bfn
)
809 (member bfn
(oref this
:include
)))))
811 (cl-defmethod ede-buffer-mine ((this project-am-man
) buffer
)
812 "Return t if object THIS lays claim to the file in BUFFER."
813 (string= (oref this
:name
)
814 (file-relative-name (buffer-file-name buffer
) (oref this
:path
))))
816 (cl-defmethod ede-buffer-mine ((this project-am-lisp
) buffer
)
817 "Return t if object THIS lays claim to the file in BUFFER."
818 (member (file-relative-name (buffer-file-name buffer
) (oref this
:path
))
819 (oref this
:source
)))
821 (cl-defmethod project-am-subtree ((ampf project-am-makefile
) subdir
)
822 "Return the sub project in AMPF specified by SUBDIR."
823 (object-assoc (expand-file-name subdir
) 'file
(oref ampf subproj
)))
825 (cl-defmethod project-compile-target-command ((this project-am-target
))
826 "Default target to use when compiling a given target."
827 ;; This is a pretty good default for most.
830 (cl-defmethod project-compile-target-command ((this project-am-objectcode
))
831 "Default target to use when compiling an object code target."
834 (cl-defmethod project-compile-target-command ((this project-am-texinfo
))
835 "Default target t- use when compiling a texinfo file."
836 (let ((n (oref this
:name
)))
837 (if (string-match "\\.texi?\\(nfo\\)?" n
)
838 (setq n
(replace-match ".info" t t n
)))
842 ;;; Generic useful functions
844 (defun project-am-last-dir (file)
845 "Return the last part of a directory name.
846 Argument FILE is the file to extract the end directory name from."
847 (let* ((s (file-name-directory file
))
848 (d (directory-file-name s
))
850 (file-name-nondirectory d
))
853 (defun project-am-preferred-target-type (file)
854 "For FILE, return the preferred type for that file."
855 (cond ((string-match "\\.texi?\\(nfo\\)$" file
)
857 ((string-match "\\.[0-9]$" file
)
859 ((string-match "\\.el$" file
)
862 'project-am-program
)))
864 (cl-defmethod ede-buffer-header-file((this project-am-objectcode
) buffer
)
865 "There are no default header files."
866 (or (cl-call-next-method)
867 (let ((s (oref this source
))
869 (while (and s
(not found
))
870 ;; Add more logic here if applicable.
871 (if (string-match "\\.\\(h\\|H\\|hh\\|hpp\\)" (car s
))
872 (setq found
(car s
)))
876 (cl-defmethod ede-documentation ((this project-am-texinfo
))
877 "Return a list of files that provides documentation.
878 Documentation is not for object THIS, but is provided by THIS for other
879 files in the project."
880 (let* ((src (append (oref this source
)
881 (oref this include
)))
882 (proj (ede-target-parent this
))
883 (dir (oref proj directory
))
885 ;; Loop over all entries and expand
888 (expand-file-name (car src
) dir
)
890 (setq src
(cdr src
)))
895 ;;; Configure.ac queries.
897 (defvar project-am-autoconf-file-options
898 '("configure.ac" "configure.in")
899 "List of possible configure files to look in for project info.")
901 (defun project-am-autoconf-file (dir)
902 "Return the name of the autoconf file to use in DIR."
904 (dolist (L project-am-autoconf-file-options
)
905 (when (file-exists-p (expand-file-name L dir
))
906 (setq ans
(expand-file-name L dir
))))
909 (defmacro project-am-with-config-current
(file &rest forms
)
910 "Set the Configure FILE in the top most directory above DIR as current.
911 Run FORMS in the configure file.
912 Kill the Configure buffer if it was not already in a buffer."
914 (let ((fb (generate-new-buffer ,file
)))
917 (insert-file-contents ,file
)
921 (put 'project-am-with-config-current
'lisp-indent-function
1)
923 (add-hook 'edebug-setup-hook
925 (def-edebug-spec project-am-with-config-current
928 (defmacro project-am-extract-shell-variable
(var)
929 "Extract the value of the shell variable VAR from a shell script."
931 (goto-char (point-min))
932 (when (re-search-forward (concat "^" (regexp-quote var
) "\\s-*=\\s-*")
934 (buffer-substring-no-properties (point) (point-at-eol)))))
936 (defun project-am-extract-package-info (dir)
937 "Extract the package information for directory DIR."
938 (let ((conf-in (project-am-autoconf-file dir
))
939 (conf-sh (expand-file-name "configure" dir
))
940 (name (file-name-nondirectory
941 (directory-file-name dir
)))
947 ;; Try configure.ac or configure.in
949 (project-am-with-config-current conf-in
950 (let ((aci (autoconf-parameters-for-macro "AC_INIT"))
951 (aia (autoconf-parameters-for-macro "AM_INIT_AUTOMAKE"))
952 (acf (autoconf-parameters-for-macro "AC_CONFIG_FILES"))
953 (aco (autoconf-parameters-for-macro "AC_OUTPUT"))
956 ;; AC init has more than 1 parameter
958 (setq name
(nth 0 aci
)
961 ;; The init automake has more than 1 parameter
963 (setq name
(nth 0 aia
)
967 ;; AC_CONFIG_FILES, or AC_OUTPUT lists everything that
968 ;; should be detected as part of this PROJECT, but not in a
969 ;; particular TARGET.
970 (let ((outfiles (cond (aco (list (car aco
)))
972 (if (> (length outfiles
) 1)
973 (setq configfiles outfiles
)
974 (setq configfiles
(split-string (car outfiles
) "\\s-" t
)))
978 ;; Else, try the script
979 ((file-exists-p conf-sh
)
980 (project-am-with-config-current conf-sh
981 (setq name
(project-am-extract-shell-variable "PACKAGE_NAME")
982 ver
(project-am-extract-shell-variable "PACKAGE_VERSION")
985 ;; Don't know what else....
989 (list name ver bugrep configfiles
)
992 (defun project-am-package-info (dir)
993 "Get the package information for directory topmost project dir over DIR.
994 Calculates the info with `project-am-extract-package-info'."
995 (let ((top (ede-toplevel)))
996 (when top
(setq dir
(oref top
:directory
)))
997 (project-am-extract-package-info dir
)))
999 ;; for simple per project include path extension
1000 (cl-defmethod ede-system-include-path ((this project-am-makefile
))
1001 "Return `project-am-localvars-include-path', usually local variable
1002 per file or in .dir-locals.el or similar."
1003 (bound-and-true-p project-am-localvars-include-path
))
1005 (cl-defmethod ede-system-include-path ((this project-am-target
))
1006 "Return `project-am-localvars-include-path', usually local variable
1007 per file or in .dir-locals.el or similar."
1008 (bound-and-true-p project-am-localvars-include-path
))
1011 (provide 'ede
/project-am
)
1014 ;; generated-autoload-load-name: "ede/project-am"
1017 ;;; ede/project-am.el ends here