xref: /386bsd/usr/local/lib/emacs/19.25/lisp/makefile.el (revision a2142627)
1;;; makefile.el --- makefile editing commands for Emacs
2
3;; Copyright (C) 1992, 1994 Free Software Foundation, Inc.
4
5;; Author: Thomas Neumann <tom@smart.bo.open.de>
6;;	Eric S. Raymond <esr@snark.thyrsus.com>
7;; Adapted-By: ESR
8;; Keywords: unix, tools
9
10;; RMS:
11;; This needs work.  The electric characters are too obnoxious.
12;; It should not define C-c LETTER.
13;; It should support knowing the list of existing macros and targets
14;; via M-TAB completion, not by preempting insertion of references.
15;; Also, the doc strings need fixing: the first line doesn't stand alone,
16;; and other usage is not high quality.  Symbol names don't have `...'.
17
18;; So, for the meantime, this is not the default mode for makefiles.
19
20;; $Id: makefile.el,v 1.16 1994/05/22 22:10:39 rms Exp $
21
22;; This file is part of GNU Emacs.
23
24;; GNU Emacs is free software; you can redistribute it and/or modify
25;; it under the terms of the GNU General Public License as published by
26;; the Free Software Foundation; either version 1, or (at your option)
27;; any later version.
28
29;; GNU Emacs is distributed in the hope that it will be useful,
30;; but WITHOUT ANY WARRANTY; without even the implied warranty of
31;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32;; GNU General Public License for more details.
33
34;; You should have received a copy of the GNU General Public License
35;; along with GNU Emacs; see the file COPYING.  If not, write to
36;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
37
38;;; Commentary:
39
40;; A major mode for editing makefiles.  The mode knows about Makefile
41;; syntax and defines M-n and M-p to move to next and previous productions.
42;;
43;; The keys $, =, : and . are electric; they try to help you fill in a
44;; macro reference, macro definition, ordinary target name, or special
45;; target name, respectively.  Such names are completed using a list of
46;; targets and macro names parsed out of the makefile.  This list is
47;; automatically updated, if necessary, whenever you invoke one of
48;; these commands.  You can force it to be updated with C-c C-p.
49;;
50;; The command C-c f adds certain filenames in the current directory
51;; as targets.  You can filter out filenames by setting the variable
52;; makefile-ignored-files-in-pickup-regex.
53;;
54;; The command C-c C-u grinds for a bit, then pops up a report buffer
55;; showing which target names are up-to-date with respect to their
56;; prerequisites, which targets are out-of-date, and which have no
57;; prerequisites.
58;;
59;; The command C-c b pops up a browser window listing all target and
60;; macro names.  You can mark or unmark items wit C-c SPC, and insert
61;; all marked items back in the Makefile with C-c TAB.
62;;
63;; The command C-c TAB in the makefile buffer inserts a GNU make builtin.
64;; You will be prompted for the builtin's args.
65;;
66;; There are numerous other customization variables.
67
68;;; Code:
69
70(provide 'makefile)
71
72;;; ------------------------------------------------------------
73;;; Configurable stuff
74;;; ------------------------------------------------------------
75
76(defconst makefile-mode-name "Makefile"
77  "The \"pretty name\" of makefile-mode, as it appears in the modeline.")
78
79(defvar makefile-browser-buffer-name "*Macros and Targets*"
80  "Name of the macro- and target browser buffer.")
81
82(defvar makefile-target-colon ":"
83  "The string that gets appended to all target names
84inserted by makefile-insert-target.
85\":\" or \"::\" are quite common values.")
86
87(defvar makefile-macro-assign " = "
88  "The string that gets appended to all macro names
89inserted by makefile-insert-macro.
90The normal value should be \" = \", since this is what
91standard make expects. However, newer makes such as dmake
92allow a larger variety of different macro assignments, so you
93might prefer to use \" += \" or \" := \" .")
94
95(defvar makefile-use-curly-braces-for-macros-p nil
96  "Controls the style of generated macro references.
97Set this variable to a non-nil value if you prefer curly braces
98in macro-references, so it looks like ${this}.  A value of nil
99will cause makefile-mode to use parentheses, making macro references
100look like $(this) .")
101
102(defvar makefile-tab-after-target-colon t
103  "If you want a TAB (instead of a space) to be appended after the
104target colon, then set this to a non-nil value.")
105
106(defvar makefile-browser-leftmost-column 10
107  "Number of blanks to the left of the browser selection mark.")
108
109(defvar makefile-browser-cursor-column 10
110  "Column in which the cursor is positioned when it moves
111up or down in the browser.")
112
113(defvar makefile-browser-selected-mark "+  "
114  "String used to mark selected entries in the browser.")
115
116(defvar makefile-browser-unselected-mark "   "
117  "String used to mark unselected entries in the browser.")
118
119(defvar makefile-browser-auto-advance-after-selection-p t
120  "If non-nil, the cursor will automagically advance to the next line after
121an item has been selected in the browser.")
122
123(defvar makefile-pickup-everything-picks-up-filenames-p nil
124  "If non-nil, makefile-pickup-everything also picks up filenames as targets
125\(i.e. it calls makefile-find-filenames-as-targets), otherwise filenames are
126omitted.")
127
128(defvar makefile-cleanup-continuations-p t
129  "If non-nil, makefile-mode will assure that no line in the file ends with a
130backslash (the continuation character) followed by any whitespace.  This is
131done by silently removing the trailing whitespace, leaving the backslash itself
132intact.  IMPORTANT: Please note that enabling this option causes makefile-mode
133to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \'it seems necessary\'.")
134
135(defvar makefile-browser-hook '()
136  "A function or list of functions to be called just before the
137browser is entered. This is executed in the makefile buffer.")
138
139;;
140;; Special targets for DMake, Sun's make ...
141;;
142(defvar makefile-special-targets-list
143  '(("DEFAULT")      ("DONE")        ("ERROR")        ("EXPORT")
144    ("FAILED")       ("GROUPEPILOG") ("GROUPPROLOG")  ("IGNORE")
145    ("IMPORT")       ("INCLUDE")     ("INCLUDEDIRS")  ("INIT")
146    ("KEEP_STATE")   ("MAKEFILES")   ("MAKE_VERSION") ("NO_PARALLEL")
147    ("PARALLEL")     ("PHONY")       ("PRECIOUS")     ("REMOVE")
148    ("SCCS_GET")     ("SILENT")      ("SOURCE")       ("SUFFIXES")
149    ("WAIT")         ("c.o")         ("C.o")          ("m.o")
150    ("el.elc")       ("y.c")         ("s.o"))
151  "List of special targets. You will be offered to complete
152on one of those in the minibuffer whenever you enter a \".\"
153at the beginning of a line in makefile-mode.")
154
155(defvar makefile-runtime-macros-list
156  '(("@") ("&") (">") ("<") ("*") ("^") ("?") ("%"))
157  "List of macros that are resolved by make at runtime.
158If you insert a macro reference using makefile-insert-macro-ref, the name
159of the macro is checked against this list. If it can be found its name will
160not be enclosed in { } or ( ).")
161
162(defconst makefile-dependency-regex
163  "^[^ \t#:]+\\([ \t]+[^ \t#:]+\\)*[ \t]*:\\([ \t]*$\\|\\([^=\n].*$\\)\\)"
164  "Regex used to find dependency lines in a makefile.")
165
166(defconst makefile-macroassign-regex
167  "^[^ \t][^:#=]*[\\*:\\+]?:?=.*$"
168  "Regex used to find macro assignment lines in a makefile.")
169
170(defconst makefile-ignored-files-in-pickup-regex
171  "\\(^\\..*\\)\\|\\(.*~$\\)\\|\\(.*,v$\\)\\|\\(\\.[chy]\\)"
172  "Regex for filenames that will NOT be included in the target list.")
173
174;;; ------------------------------------------------------------
175;;; The following configurable variables are used in the
176;;; up-to-date overview .
177;;; The standard configuration assumes that your `make' program
178;;; can be run in question/query mode using the `-q' option, this
179;;; means that the command
180;;;
181;;;    make -q foo
182;;;
183;;; should return an exit status of zero if the target `foo' is
184;;; up to date and a nonzero exit status otherwise.
185;;; Many makes can do this although the docs/manpages do not mention
186;;; it. Try it with your favourite one.  GNU make, System V make, and
187;;; Dennis Vadura's DMake have no problems.
188;;; Set the variable `makefile-brave-make' to the name of the
189;;; make utility that does this on your system.
190;;; To understand what this is all about see the function definition
191;;; of `makefile-query-by-make-minus-q' .
192;;; ------------------------------------------------------------
193
194(defvar makefile-brave-make "make"
195  "A make that can handle the \'-q\' option.")
196
197(defvar makefile-query-one-target-method 'makefile-query-by-make-minus-q
198  "A function symbol [one that can be used as the first argument to
199funcall] that provides a function that must conform to the following
200interface:
201
202* As its first argument, it must accept the name of the target to
203  be checked, as a string.
204
205* As its second argument, it may accept the name of a makefile
206  as a string. Depending on what you're going to do you may
207  not need this.
208
209* It must return the integer value 0 (zero) if the given target
210  should be considered up-to-date in the context of the given
211  makefile, any nonzero integer value otherwise.")
212
213(defvar makefile-up-to-date-buffer-name "*Makefile Up-to-date overview*"
214  "Name of the Up-to-date overview buffer.")
215
216;;; --- end of up-to-date-overview configuration ------------------
217
218(defvar makefile-mode-map nil
219  "The keymap that is used in makefile-mode.")
220(if makefile-mode-map
221    ()
222  (setq makefile-mode-map (make-sparse-keymap))
223  ;; set up the keymap
224  (define-key makefile-mode-map "$"        'makefile-insert-macro-ref)
225  (define-key makefile-mode-map "\C-c:"    'makefile-insert-target-ref)
226  (define-key makefile-mode-map ":"        'makefile-electric-colon)
227  (define-key makefile-mode-map "="        'makefile-electric-equal)
228  (define-key makefile-mode-map "."        'makefile-electric-dot)
229  (define-key makefile-mode-map "\C-c\C-f" 'makefile-pickup-filenames-as-targets)
230  (define-key makefile-mode-map "\C-c\C-b" 'makefile-switch-to-browser)
231  (define-key makefile-mode-map "\C-c\C-p" 'makefile-pickup-everything)
232  (define-key makefile-mode-map "\C-c\C-u" 'makefile-create-up-to-date-overview)
233  (define-key makefile-mode-map "\C-c\C-i" 'makefile-insert-gmake-function)
234  (define-key makefile-mode-map "\M-p"     'makefile-previous-dependency)
235  (define-key makefile-mode-map "\M-n"     'makefile-next-dependency))
236
237(defvar makefile-browser-map nil
238  "The keymap that is used in the macro- and target browser.")
239(if makefile-browser-map
240    ()
241  (setq makefile-browser-map (make-sparse-keymap))
242  (define-key makefile-browser-map "n"    'makefile-browser-next-line)
243  (define-key makefile-browser-map "\C-n" 'makefile-browser-next-line)
244  (define-key makefile-browser-map "p"    'makefile-browser-previous-line)
245  (define-key makefile-browser-map "\C-p" 'makefile-browser-previous-line)
246  (define-key makefile-browser-map " "    'makefile-browser-toggle)
247  (define-key makefile-browser-map "i"    'makefile-browser-insert-selection)
248  (define-key makefile-browser-map "I"    'makefile-browser-insert-selection-and-quit)
249  (define-key makefile-browser-map "\C-c\C-m" 'makefile-browser-insert-continuation)
250  (define-key makefile-browser-map "q"    'makefile-browser-quit)
251  ;; disable horizontal movement
252  (define-key makefile-browser-map "\C-b" 'undefined)
253  (define-key makefile-browser-map "\C-f" 'undefined))
254
255
256(defvar makefile-mode-syntax-table nil
257  "The syntax-table used in makefile mode.")
258(if makefile-mode-syntax-table
259    ()
260  (setq makefile-mode-syntax-table (make-syntax-table))
261  (modify-syntax-entry ?\( "()    " makefile-mode-syntax-table)
262  (modify-syntax-entry ?\) ")(    " makefile-mode-syntax-table)
263  (modify-syntax-entry ?\[ "(]    " makefile-mode-syntax-table)
264  (modify-syntax-entry ?\] "([    " makefile-mode-syntax-table)
265  (modify-syntax-entry ?\{ "(}    " makefile-mode-syntax-table)
266  (modify-syntax-entry ?\} "){    " makefile-mode-syntax-table)
267  (modify-syntax-entry ?#  "<     " makefile-mode-syntax-table)
268  (modify-syntax-entry ?\n ">     " makefile-mode-syntax-table))
269
270
271;;; ------------------------------------------------------------
272;;; Internal variables.
273;;; You don't need to configure below this line.
274;;; ------------------------------------------------------------
275
276(defvar makefile-target-table nil
277  "Table of all targets that have been inserted in
278this Makefile buffer using makefile-insert-target or picked up
279using makefile-pickup-targets.")
280
281(defvar makefile-macro-table nil
282  "Table of all macros that have been iserted in
283this Makefile buffer using makefile-insert-macro or picked up
284using makefile-pickup-macros.")
285
286(defvar makefile-browser-client
287  "A buffer in makefile-mode that is currently using the browser.")
288
289(defvar makefile-browser-selection-vector nil)
290(defvar makefile-has-prereqs nil)
291(defvar makefile-need-target-pickup t)
292(defvar makefile-need-macro-pickup t)
293
294(defvar makefile-mode-hook '())
295
296(defconst makefile-gnumake-functions-alist
297  '(
298    ;; Text functions
299    ("subst" "From" "To" "In")
300    ("patsubst" "Pattern" "Replacement" "In")
301    ("strip" "Text")
302    ("findstring" "Find what" "In")
303    ("filter" "Pattern" "Text")
304    ("filter-out" "Pattern" "Text")
305    ("sort" "List")
306    ;; Filename functions
307    ("dir" "Names")
308    ("notdir" "Names")
309    ("suffix" "Names")
310    ("basename" "Names")
311    ("addsuffix" "Suffix" "Names")
312    ("join" "List 1" "List 2")
313    ("word" "Index" "Text")
314    ("words" "Text")
315    ("firstword" "Text")
316    ("wildcard" "Pattern")
317    ;; Misc functions
318    ("foreach" "Variable" "List" "Text")
319    ("origin" "Variable")
320    ("shell" "Command"))
321  "A list of GNU make function names associated with
322the prompts for each function.
323This is used in the function makefile-insert-gmake-function .")
324
325
326;;; ------------------------------------------------------------
327;;; The mode function itself.
328;;; ------------------------------------------------------------
329
330;;;###autoload
331(defun makefile-mode ()
332  "Major mode for editing Makefiles.
333Calling this function invokes the function(s) \"makefile-mode-hook\" before
334doing anything else.
335
336\\{makefile-mode-map}
337
338In the browser, use the following keys:
339
340\\{makefile-browser-map}
341
342makefile-mode can be configured by modifying the following
343variables:
344
345makefile-mode-name:
346    The \"pretty name\" of makefile-mode, as it
347    appears in the modeline.
348
349makefile-browser-buffer-name:
350    Name of the macro- and target browser buffer.
351
352makefile-target-colon:
353    The string that gets appended to all target names
354    inserted by makefile-insert-target.
355    \":\" or \"::\" are quite common values.
356
357makefile-macro-assign:
358   The string that gets appended to all macro names
359   inserted by makefile-insert-macro.
360   The normal value should be \" = \", since this is what
361   standard make expects. However, newer makes such as dmake
362   allow a larger variety of different macro assignments, so you
363   might prefer to use \" += \" or \" := \" .
364
365makefile-tab-after-target-colon:
366   If you want a TAB (instead of a space) to be appended after the
367   target colon, then set this to a non-nil value.
368
369makefile-browser-leftmost-column:
370   Number of blanks to the left of the browser selection mark.
371
372makefile-browser-cursor-column:
373   Column in which the cursor is positioned when it moves
374   up or down in the browser.
375
376makefile-browser-selected-mark:
377   String used to mark selected entries in the browser.
378
379makefile-browser-unselected-mark:
380   String used to mark unselected entries in the browser.
381
382makefile-browser-auto-advance-after-selection-p:
383   If this variable is set to a non-nil value the cursor
384   will automagically advance to the next line after an item
385   has been selected in the browser.
386
387makefile-pickup-everything-picks-up-filenames-p:
388   If this variable is set to a non-nil value then
389   makefile-pickup-everything also picks up filenames as targets
390   (i.e. it calls makefile-find-filenames-as-targets), otherwise
391   filenames are omitted.
392
393makefile-cleanup-continuations-p:
394   If this variable is set to a non-nil value then makefile-mode
395   will assure that no line in the file ends with a backslash
396   (the continuation character) followed by any whitespace.
397   This is done by silently removing the trailing whitespace, leaving
398   the backslash itself intact.
399   IMPORTANT: Please note that enabling this option causes makefile-mode
400   to MODIFY A FILE WITHOUT YOUR CONFIRMATION when \'it seems necessary\'.
401
402makefile-browser-hook:
403   A function or list of functions to be called just before the
404   browser is entered. This is executed in the makefile buffer.
405
406makefile-special-targets-list:
407   List of special targets. You will be offered to complete
408   on one of those in the minibuffer whenever you enter a \".\"
409   at the beginning of a line in makefile-mode."
410  (interactive)
411  (kill-all-local-variables)
412  (make-local-variable 'local-write-file-hooks)
413  (setq local-write-file-hooks
414	'(makefile-cleanup-continuations makefile-warn-suspicious-lines))
415  (make-local-variable 'makefile-target-table)
416  (make-local-variable 'makefile-macro-table)
417  (make-local-variable 'makefile-has-prereqs)
418  (make-local-variable 'makefile-need-target-pickup)
419  (make-local-variable 'makefile-need-macro-pickup)
420  (make-local-variable 'comment-start)
421  (make-local-variable 'comment-end)
422  (make-local-variable 'comment-start-skip)
423  (setq comment-start "#")
424  (setq comment-end "")
425  (setq comment-start-skip "#[ \t]*")
426  ;; become the current major mode
427  (setq major-mode 'makefile-mode)
428  (setq mode-name makefile-mode-name)
429  ;; activate keymap
430  (use-local-map makefile-mode-map)
431  (set-syntax-table makefile-mode-syntax-table)
432  (setq indent-tabs-mode t)		;real TABs are important in makefiles
433  (run-hooks 'makefile-mode-hook))
434
435(defun makefile-next-dependency ()
436  "Move (point) to the beginning of the next dependency line below (point)."
437  (interactive)
438  (let ((here (point)))
439    (end-of-line)
440    (if (re-search-forward makefile-dependency-regex (point-max) t)
441	(progn (beginning-of-line) t)	; indicate success
442      (goto-char here) nil)))
443
444(defun makefile-previous-dependency ()
445  "Move (point) to the beginning of the next dependency line above (point)."
446  (interactive)
447  (let ((here (point)))
448    (beginning-of-line)
449    (if (re-search-backward makefile-dependency-regex (point-min) t)
450	(progn (beginning-of-line) t)	; indicate success
451      (goto-char here) nil)))
452
453
454;;; Stuff below here depends on the pickup state
455
456(defun makefile-electric-dot ()
457  "At (bol), offer completion on makefile-special-targets-list.
458Anywhere else just insert a dot."
459  (interactive)
460  (if (bolp)
461      (makefile-insert-special-target)
462    (insert ".")))
463
464(defun makefile-insert-special-target ()
465  "Complete on makefile-special-targets-list, insert result at (point)."
466  (interactive)
467  (makefile-pickup-targets)
468  (let
469      ((special-target
470       (completing-read "Special target: "
471			makefile-special-targets-list nil nil nil)))
472    (if (zerop (length special-target))
473	()
474      (insert (format ".%s:" special-target))
475      (makefile-forward-after-target-colon))))
476
477(defun makefile-electric-equal ()
478  "At (bol) do makefile-insert-macro.  Anywhere else just self-insert."
479  (interactive)
480  (makefile-pickup-macros)
481  (if (bolp)
482      (call-interactively 'makefile-insert-macro)
483    (insert "=")))
484
485(defun makefile-insert-macro (macro-name)
486  "Prepare definition of a new macro."
487  (interactive "sMacro Name: ")
488  (makefile-pickup-macros)
489  (if (not (zerop (length macro-name)))
490      (progn
491	(beginning-of-line)
492	(insert (format "%s%s" macro-name makefile-macro-assign))
493	(setq makefile-need-macro-pickup t)
494	(makefile-remember-macro macro-name))))
495
496(defun makefile-insert-macro-ref (macro-name)
497  "Complete on a list of known macros, then insert complete ref at (point)."
498  (interactive
499   (list
500    (progn
501      (makefile-pickup-macros)
502      (completing-read "Refer to macro: " makefile-macro-table nil nil nil))))
503   (if (not (zerop (length macro-name)))
504       (if (assoc macro-name makefile-runtime-macros-list)
505	   (insert (format "$%s" macro-name))
506	 (insert (makefile-format-macro-ref macro-name)))))
507
508(defun makefile-insert-target (target-name)
509  "Prepare definition of a new target (dependency line)."
510  (interactive "sTarget: ")
511  (if (not (zerop (length target-name)))
512      (progn
513	(beginning-of-line)
514	(insert (format "%s%s" target-name makefile-target-colon))
515	(makefile-forward-after-target-colon)
516	(end-of-line)
517	(setq makefile-need-target-pickup t)
518	(makefile-remember-target target-name))))
519
520(defun makefile-insert-target-ref (target-name)
521  "Complete on a list of known targets, then insert target-ref at (point) ."
522  (interactive
523   (list
524    (progn
525     (makefile-pickup-targets)
526     (completing-read "Refer to target: " makefile-target-table nil nil nil))))
527   (if (not (zerop (length target-name)))
528       (progn
529	 (insert (format "%s " target-name)))))
530
531(defun makefile-electric-colon ()
532  "At (bol) defines a new target, anywhere else just self-insert ."
533  (interactive)
534  (if (bolp)
535      (call-interactively 'makefile-insert-target)
536    (insert ":")))
537
538;;; ------------------------------------------------------------
539;;; Extracting targets and macros from an existing makefile
540;;; ------------------------------------------------------------
541
542(defun makefile-pickup-targets ()
543  "Scan a buffer that contains a makefile for target definitions (dependencies)
544and add them to the list of known targets."
545  (interactive)
546  (if (not makefile-need-target-pickup)
547      nil
548    (setq makefile-need-target-pickup nil)
549    (setq makefile-target-table nil)
550    (setq makefile-has-prereqs nil)
551    (save-excursion
552      (goto-char (point-min))
553      (while (re-search-forward makefile-dependency-regex (point-max) t)
554	(makefile-add-this-line-targets)))
555    (message "Read targets OK.")))
556
557(defun makefile-add-this-line-targets ()
558  (save-excursion
559    (beginning-of-line)
560    (let ((done-with-line nil)
561	  (line-number (1+ (count-lines (point-min) (point)))))
562      (while (not done-with-line)
563	(skip-chars-forward " \t")
564	(if (not (setq done-with-line (or (eolp)
565					  (char-equal (char-after (point)) ?:))))
566	    (progn
567	      (let* ((start-of-target-name (point))
568		     (target-name
569		      (progn
570			(skip-chars-forward "^ \t:#")
571			(buffer-substring start-of-target-name (point))))
572		     (has-prereqs
573		      (not (looking-at ":[ \t]*$"))))
574		(if (makefile-remember-target target-name has-prereqs)
575		    (message "Picked up target \"%s\" from line %d"
576			     target-name line-number)))))))))
577
578
579(defun makefile-pickup-macros ()
580  "Scan a buffer that contains a makefile for macro definitions
581and add them to the list of known macros."
582  (interactive)
583  (if (not makefile-need-macro-pickup)
584      nil
585    (setq makefile-need-macro-pickup nil)
586    (setq makefile-macro-table nil)
587    (save-excursion
588      (goto-char (point-min))
589      (while (re-search-forward makefile-macroassign-regex (point-max) t)
590	(makefile-add-this-line-macro)
591	(forward-line 1)))
592    (message "Read macros OK.")))
593
594(defun makefile-add-this-line-macro ()
595  (save-excursion
596    (beginning-of-line)
597    (skip-chars-forward " \t")
598    (if (not (eolp))
599	(let* ((start-of-macro-name (point))
600	       (line-number (1+ (count-lines (point-min) (point))))
601	       (macro-name (progn
602			     (skip-chars-forward "^ \t:#=*")
603			     (buffer-substring start-of-macro-name (point)))))
604	  (if (makefile-remember-macro macro-name)
605	      (message "Picked up macro \"%s\" from line %d"
606		       macro-name line-number))))))
607
608(defun makefile-pickup-everything ()
609  "Calls makefile-pickup-targets and makefile-pickup-macros.
610See their documentation for what they do."
611  (interactive)
612  (makefile-pickup-macros)
613  (makefile-pickup-targets)
614  (if makefile-pickup-everything-picks-up-filenames-p
615      (makefile-pickup-filenames-as-targets)))
616
617
618(defun makefile-pickup-filenames-as-targets ()
619  "Scan the current directory for filenames, check each filename
620against makefile-ignored-files-in-pickup-regex and add all qualifying
621names to the list of known targets."
622  (interactive)
623  (let* ((dir (file-name-directory (buffer-file-name)))
624	 (raw-filename-list (if dir
625				(file-name-all-completions "" dir)
626			      (file-name-all-completions "" ""))))
627    (mapcar '(lambda (name)
628	       (if (and (not (file-directory-p name))
629			(not (string-match makefile-ignored-files-in-pickup-regex
630					   name)))
631		   (if (makefile-remember-target name)
632		       (message "Picked up file \"%s\" as target" name))))
633	    raw-filename-list)))
634
635;;; ------------------------------------------------------------
636;;; The browser window
637;;; ------------------------------------------------------------
638
639(defun makefile-browser-format-target-line (target selected)
640  (format
641   (concat (make-string makefile-browser-leftmost-column ?\ )
642	   (if selected
643	       makefile-browser-selected-mark
644	     makefile-browser-unselected-mark)
645	   "%s%s")
646   target makefile-target-colon))
647
648(defun makefile-browser-format-macro-line (macro selected)
649  (format
650   (concat (make-string makefile-browser-leftmost-column ?\ )
651	   (if selected
652	       makefile-browser-selected-mark
653	     makefile-browser-unselected-mark)
654	   (makefile-format-macro-ref macro))))
655
656(defun makefile-browser-fill (targets macros)
657  (let ((inhibit-read-only t))
658    (goto-char (point-min))
659    (erase-buffer)
660    (mapconcat
661     (function
662      (lambda (item) (insert (makefile-browser-format-target-line (car item) nil) "\n")))
663     targets
664     "")
665    (mapconcat
666     (function
667      (lambda (item) (insert (makefile-browser-format-macro-line (car item) nil) "\n")))
668     macros
669     "")
670    (sort-lines nil (point-min) (point-max))
671    (goto-char (1- (point-max)))
672    (delete-char 1)			; remove unnecessary newline at eob
673    (goto-char (point-min))
674    (forward-char makefile-browser-cursor-column)))
675
676;;;
677;;; Moving up and down in the browser
678;;;
679
680(defun makefile-browser-next-line ()
681  "Move the browser selection cursor to the next line."
682  (interactive)
683  (if (not (makefile-last-line-p))
684      (progn
685	(forward-line 1)
686	(forward-char makefile-browser-cursor-column))))
687
688(defun makefile-browser-previous-line ()
689  "Move the browser selection cursor to the previous line."
690  (interactive)
691  (if (not (makefile-first-line-p))
692      (progn
693	(forward-line -1)
694	(forward-char makefile-browser-cursor-column))))
695
696;;;
697;;; Quitting the browser (returns to client buffer)
698;;;
699
700(defun makefile-browser-quit ()
701  "Leave the makefile-browser-buffer and return to the buffer
702from that it has been entered."
703  (interactive)
704  (let ((my-client makefile-browser-client))
705    (setq makefile-browser-client nil)	; we quitted, so NO client!
706    (set-buffer-modified-p nil)
707    (kill-buffer (current-buffer))
708    (pop-to-buffer my-client)))
709
710;;;
711;;; Toggle state of a browser item
712;;;
713
714(defun makefile-browser-toggle ()
715  "Toggle the selection state of the browser item at the cursor position."
716  (interactive)
717  (let ((this-line (count-lines (point-min) (point))))
718    (setq this-line (max 1 this-line))
719    (makefile-browser-toggle-state-for-line this-line)
720    (goto-line this-line)
721    (let ((inhibit-read-only t))
722      (beginning-of-line)
723      (if (makefile-browser-on-macro-line-p)
724	  (let ((macro-name (makefile-browser-this-line-macro-name)))
725	    (kill-line)
726	    (insert
727	     (makefile-browser-format-macro-line
728		macro-name
729		(makefile-browser-get-state-for-line this-line))))
730	(let ((target-name (makefile-browser-this-line-target-name)))
731	  (kill-line)
732	  (insert
733	   (makefile-browser-format-target-line
734	      target-name
735	      (makefile-browser-get-state-for-line this-line))))))
736    (beginning-of-line)
737    (forward-char makefile-browser-cursor-column)
738    (if makefile-browser-auto-advance-after-selection-p
739	(makefile-browser-next-line))))
740
741;;;
742;;; Making insertions into the client buffer
743;;;
744
745(defun makefile-browser-insert-continuation ()
746  "Insert a makefile continuation.
747In the browser\'s client buffer, go to (end-of-line), insert a \'\\\'
748character, insert a new blank line, go to that line and indent by one TAB.
749This is most useful in the process of creating continued lines when copying
750large dependencies from the browser to the client buffer.
751\(point) advances accordingly in the client buffer."
752  (interactive)
753  (save-excursion
754    (set-buffer makefile-browser-client)
755    (end-of-line)
756    (insert "\\\n\t")))
757
758(defun makefile-browser-insert-selection ()
759  "Insert all browser-selected targets and/or macros in the browser\'s
760client buffer.
761Insertion takes place at (point)."
762  (interactive)
763  (save-excursion
764    (goto-line 1)
765    (let ((current-line 1))
766      (while (not (eobp))
767	(if (makefile-browser-get-state-for-line current-line)
768	    (makefile-browser-send-this-line-item))
769	(forward-line 1)
770	(setq current-line (1+ current-line))))))
771
772(defun makefile-browser-insert-selection-and-quit ()
773  (interactive)
774  (makefile-browser-insert-selection)
775  (makefile-browser-quit))
776
777(defun makefile-browser-send-this-line-item ()
778  (if (makefile-browser-on-macro-line-p)
779      (save-excursion
780	(let ((macro-name (makefile-browser-this-line-macro-name)))
781	  (set-buffer makefile-browser-client)
782	  (insert (makefile-format-macro-ref macro-name) " ")))
783    (save-excursion
784      (let ((target-name (makefile-browser-this-line-target-name)))
785	(set-buffer makefile-browser-client)
786	(insert target-name " ")))))
787
788(defun makefile-browser-start-interaction ()
789  (use-local-map makefile-browser-map)
790  (setq buffer-read-only t))
791
792(defun makefile-browse (targets macros)
793  (interactive)
794  (if (zerop (+ (length targets) (length macros)))
795      (progn
796	(beep)
797	(message "No macros or targets to browse! Consider running 'makefile-pickup-everything\'"))
798    (let ((browser-buffer (get-buffer-create makefile-browser-buffer-name)))
799	(pop-to-buffer browser-buffer)
800	(make-variable-buffer-local 'makefile-browser-selection-vector)
801	(makefile-browser-fill targets macros)
802	(shrink-window-if-larger-than-buffer)
803	(setq makefile-browser-selection-vector
804	      (make-vector (+ (length targets) (length macros)) nil))
805	(makefile-browser-start-interaction))))
806
807(defun makefile-switch-to-browser ()
808  (interactive)
809  (run-hooks 'makefile-browser-hook)
810  (setq makefile-browser-client (current-buffer))
811  (makefile-pickup-targets)
812  (makefile-pickup-macros)
813  (makefile-browse makefile-target-table makefile-macro-table))
814
815
816;;; ------------------------------------------------------------
817;;; Up-to-date overview buffer
818;;; ------------------------------------------------------------
819
820(defun makefile-create-up-to-date-overview ()
821  "Create a buffer containing an overview of the state of all known targets.
822Known targets are targets that are explicitly defined in that makefile;
823in other words, all targets that appear on the left hand side of a
824dependency in the makefile."
825  (interactive)
826  (if (y-or-n-p "Are you sure that the makefile being edited is consistent? ")
827      ;;
828      ;; The rest of this function operates on a temporary makefile, created by
829      ;; writing the current contents of the makefile buffer.
830      ;;
831      (let ((saved-target-table makefile-target-table)
832	    (this-buffer (current-buffer))
833	    (makefile-up-to-date-buffer
834	     (get-buffer-create makefile-up-to-date-buffer-name))
835	    (filename (makefile-save-temporary))
836	    ;;
837	    ;; Forget the target table because it may contain picked-up filenames
838	    ;; that are not really targets in the current makefile.
839	    ;; We don't want to query these, so get a new target-table with just the
840	    ;; targets that can be found in the makefile buffer.
841	    ;; The 'old' target table will be restored later.
842	    ;;
843	    (real-targets (progn
844			    (makefile-pickup-targets)
845			    makefile-target-table))
846	    (prereqs makefile-has-prereqs)
847	    )
848
849	(set-buffer makefile-up-to-date-buffer)
850	(setq buffer-read-only nil)
851	(erase-buffer)
852	(makefile-query-targets filename real-targets prereqs)
853	(if (zerop (buffer-size))		; if it did not get us anything
854	    (progn
855	      (kill-buffer (current-buffer))
856	      (message "No overview created!")))
857	(set-buffer this-buffer)
858	(setq makefile-target-table saved-target-table)
859	(if (get-buffer makefile-up-to-date-buffer-name)
860	    (progn
861	      (pop-to-buffer (get-buffer makefile-up-to-date-buffer-name))
862	      (shrink-window-if-larger-than-buffer)
863	      (sort-lines nil (point-min) (point-max))
864	      (setq buffer-read-only t))))))
865
866(defun makefile-save-temporary ()
867  "Create a temporary file from the current makefile buffer."
868  (let ((filename (makefile-generate-temporary-filename)))
869    (write-region (point-min) (point-max) filename nil 0)
870    filename))				; return the filename
871
872(defun makefile-generate-temporary-filename ()
873  "Create a filename suitable for use in makefile-save-temporary.
874Be careful to allow brain-dead file systems (DOS, SYSV ...) to cope
875with the generated name !"
876  (let ((my-name (user-login-name))
877	(my-uid (int-to-string (user-uid))))
878    (concat "mktmp"
879	  (if (> (length my-name) 3)
880	      (substring my-name 0 3)
881	    my-name)
882	  "."
883	  (if (> (length my-uid) 3)
884	      (substring my-uid 0 3)
885	    my-uid))))
886
887(defun makefile-query-targets (filename target-table prereq-list)
888  "This function fills the up-to-date-overview-buffer.
889It checks each target in target-table using makefile-query-one-target-method
890and generates the overview, one line per target name."
891  (insert
892   (mapconcat
893    (function (lambda (item)
894		(let* ((target-name (car item))
895		       (no-prereqs (not (member target-name prereq-list)))
896		       (needs-rebuild (or no-prereqs
897					  (funcall
898					   makefile-query-one-target-method
899					   target-name
900					   filename))))
901		  (format "\t%s%s"
902			  target-name
903			  (cond (no-prereqs "  .. has no prerequisites")
904				(needs-rebuild "  .. NEEDS REBUILD")
905				(t "  .. is up to date"))))
906		))
907    target-table "\n"))
908  (goto-char (point-min))
909  (delete-file filename))		; remove the tmpfile
910
911(defun makefile-query-by-make-minus-q (target &optional filename)
912  (not (zerop (call-process makefile-brave-make nil nil nil "-f" filename "-q" target))))
913
914;;; ------------------------------------------------------------
915;;; Continuation cleanup
916;;; ------------------------------------------------------------
917
918(defun makefile-cleanup-continuations ()
919  (if (eq major-mode 'makefile-mode)
920      (if (and makefile-cleanup-continuations-p
921	       (not buffer-read-only))
922	  (save-excursion
923	    (goto-char (point-min))
924	    (while (re-search-forward "\\\\[ \t]+$" (point-max) t)
925	      (replace-match "\\" t t))))))
926
927
928;;; ------------------------------------------------------------
929;;; Warn of suspicious lines
930;;; ------------------------------------------------------------
931
932(defun makefile-warn-suspicious-lines ()
933  (let ((dont-save nil))
934    (if (eq major-mode 'makefile-mode)
935	(let ((suspicious
936	       (save-excursion
937		 (goto-char (point-min))
938		 (re-search-forward
939		  "\\(^[\t]+$\\)\\|\\(^[ ]+[\t]\\)" (point-max) t))))
940	  (if suspicious
941	      (let ((line-nr (count-lines (point-min) suspicious)))
942		(setq dont-save
943		      (not (y-or-n-p
944			    (format "Suspicious line %d. Save anyway "
945				    line-nr))))))))
946    dont-save))
947
948
949;;; ------------------------------------------------------------
950;;; GNU make function support
951;;; ------------------------------------------------------------
952
953(defun makefile-insert-gmake-function ()
954  "This function is intended to help you using the numerous
955macro-like \'function calls\' of GNU make.
956It will ask you for the name of the function you wish to
957use (with completion), then, after you selected the function,
958it will prompt you for all required parameters.
959This function \'knows\' about the required parameters of every
960GNU make function and will use meaningfull prompts for the
961various args, making it much easier to take advantage of this
962powerful GNU make feature."
963  (interactive)
964  (let* ((gm-function-name (completing-read
965			     "Function: "
966			     makefile-gnumake-functions-alist
967			     nil t nil))
968	 (gm-function-prompts
969	  (cdr (assoc gm-function-name makefile-gnumake-functions-alist))))
970    (if (not (zerop (length gm-function-name)))
971	(insert (makefile-format-macro-ref
972		 (concat gm-function-name " "
973			 (makefile-prompt-for-gmake-funargs
974			    gm-function-name gm-function-prompts)))
975		" "))))
976
977(defun makefile-prompt-for-gmake-funargs (function-name prompt-list)
978  (mapconcat
979   (function (lambda (one-prompt)
980	       (read-string (format "[%s] %s: " function-name one-prompt) nil)))
981   prompt-list
982   ","))
983
984
985
986;;; ------------------------------------------------------------
987;;; Utility functions
988;;; ------------------------------------------------------------
989
990(defun makefile-remember-target (target-name &optional has-prereqs)
991  "Remember a given target if it is not already remembered for this buffer."
992  (if (not (zerop (length target-name)))
993      (progn
994      (if (not (assoc target-name makefile-target-table))
995	  (setq makefile-target-table
996		(cons (list target-name) makefile-target-table)))
997      (if has-prereqs
998	  (setq makefile-has-prereqs
999		(cons target-name makefile-has-prereqs))))))
1000
1001(defun makefile-remember-macro (macro-name)
1002  "Remember a given macro if it is not already remembered for this buffer."
1003  (if (not (zerop (length macro-name)))
1004      (if (not (assoc macro-name makefile-macro-table))
1005	  (setq makefile-macro-table
1006		(cons (list macro-name) makefile-macro-table)))))
1007
1008(defun makefile-forward-after-target-colon ()
1009"Move point forward after the terminating colon
1010of a target has been inserted.
1011This accts according to the value of makefile-tab-after-target-colon ."
1012  (if makefile-tab-after-target-colon
1013      (insert "\t")
1014    (insert " ")))
1015
1016(defun makefile-browser-on-macro-line-p ()
1017  "Determine if point is on a macro line in the browser."
1018  (save-excursion
1019    (beginning-of-line)
1020    (re-search-forward "\\$[{(]" (makefile-end-of-line-point) t)))
1021
1022(defun makefile-browser-this-line-target-name ()
1023  "Extract the target name from a line in the browser."
1024  (save-excursion
1025    (end-of-line)
1026    (skip-chars-backward "^ \t")
1027    (buffer-substring (point) (1- (makefile-end-of-line-point)))))
1028
1029(defun makefile-browser-this-line-macro-name ()
1030  "Extract the macro name from a line in the browser."
1031  (save-excursion
1032    (beginning-of-line)
1033    (re-search-forward "\\$[{(]" (makefile-end-of-line-point) t)
1034    (let ((macro-start (point)))
1035      (skip-chars-forward "^})")
1036      (buffer-substring macro-start (point)))))
1037
1038(defun makefile-format-macro-ref (macro-name)
1039  "Format a macro reference according to the value of the
1040configuration variable makefile-use-curly-braces-for-macros-p ."
1041  (if (or (char-equal ?\( (string-to-char macro-name))
1042	  (char-equal ?\{ (string-to-char macro-name)))
1043      (format "$%s" macro-name)
1044    (if makefile-use-curly-braces-for-macros-p
1045	(format "${%s}" macro-name)
1046      (format "$(%s)" macro-name))))
1047
1048(defun makefile-browser-get-state-for-line (n)
1049  (aref makefile-browser-selection-vector (1- n)))
1050
1051(defun makefile-browser-set-state-for-line (n to-state)
1052  (aset makefile-browser-selection-vector (1- n) to-state))
1053
1054(defun makefile-browser-toggle-state-for-line (n)
1055  (makefile-browser-set-state-for-line n (not (makefile-browser-get-state-for-line n))))
1056
1057(defun makefile-beginning-of-line-point ()
1058  (save-excursion
1059    (beginning-of-line)
1060    (point)))
1061
1062(defun makefile-end-of-line-point ()
1063  (save-excursion
1064    (end-of-line)
1065    (point)))
1066
1067(defun makefile-last-line-p ()
1068  (= (makefile-end-of-line-point) (point-max)))
1069
1070(defun makefile-first-line-p ()
1071  (= (makefile-beginning-of-line-point) (point-min)))
1072
1073;; makefile.el ends here
1074