Commit | Line | Data |
---|---|---|
acc33231 CY |
1 | ;;; ede/autoconf-edit.el --- Keymap for autoconf |
2 | ||
acaf905b | 3 | ;; Copyright (C) 1998-2000, 2009-2012 Free Software Foundation, Inc. |
acc33231 CY |
4 | |
5 | ;; Author: Eric M. Ludlam <zappo@gnu.org> | |
6 | ;; Keywords: project | |
7 | ||
8 | ;; This file is part of GNU Emacs. | |
9 | ||
10 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
11 | ;; it under the terms of the GNU General Public License as published by | |
12 | ;; the Free Software Foundation, either version 3 of the License, or | |
13 | ;; (at your option) any later version. | |
14 | ||
15 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
16 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | ;; GNU General Public License for more details. | |
19 | ||
20 | ;; You should have received a copy of the GNU General Public License | |
21 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
22 | ||
23 | ;;; Commentary: | |
24 | ;; | |
25 | ;; Autoconf editing and modification support, and compatibility layer | |
26 | ;; for Emacses w/out autoconf mode built in. | |
27 | ||
28 | ;;; Code: | |
29 | (require 'autoconf) | |
cb85c0d8 EL |
30 | (declare-function ede-srecode-setup "ede/srecode") |
31 | (declare-function ede-srecode-insert "ede/srecode") | |
acc33231 CY |
32 | |
33 | (defun autoconf-new-program (rootdir program testfile) | |
c4444d16 | 34 | "Initialize a new configure.ac in ROOTDIR for PROGRAM using TESTFILE. |
acc33231 CY |
35 | ROOTDIR is the root directory of a given autoconf controlled project. |
36 | PROGRAM is the program to be configured. | |
37 | TESTFILE is the file used with AC_INIT. | |
38 | configure the initial configure script using `autoconf-new-automake-string'" | |
39 | (interactive "DRoot Dir: \nsProgram: \nsTest File: ") | |
cb85c0d8 | 40 | (require 'ede/srecode) |
acc33231 CY |
41 | (if (bufferp rootdir) |
42 | (set-buffer rootdir) | |
43 | (let ((cf1 (expand-file-name "configure.in" rootdir)) | |
44 | (cf2 (expand-file-name "configure.ac" rootdir))) | |
45 | (if (and (or (file-exists-p cf1) (file-exists-p cf2)) | |
46 | (not (y-or-n-p (format "File %s exists. Start Over? " | |
47 | (if (file-exists-p cf1) | |
48 | cf1 cf2) | |
49 | )))) | |
50 | (error "Quit")) | |
51 | (find-file cf2))) | |
52 | ;; Note, we only ask about overwrite if a string/path is specified. | |
53 | (erase-buffer) | |
cb85c0d8 EL |
54 | (ede-srecode-setup) |
55 | (ede-srecode-insert | |
56 | "file:ede-empty" | |
57 | "TEST_FILE" testfile | |
58 | "PROGRAM" program) | |
59 | ) | |
acc33231 CY |
60 | |
61 | (defvar autoconf-preferred-macro-order | |
62 | '("AC_INIT" | |
63 | "AM_INIT_AUTOMAKE" | |
64 | "AM_CONFIG_HEADER" | |
65 | ;; Arg parsing | |
66 | "AC_ARG_ENABLE" | |
67 | "AC_ARG_WITH" | |
68 | ;; Programs | |
69 | "AC_PROG_MAKE_SET" | |
70 | "AC_PROG_AWK" | |
71 | "AC_PROG_CC" | |
72 | "AC_PROG_CC_C_O" | |
73 | "AC_PROG_CPP" | |
74 | "AC_PROG_CXX" | |
75 | "AC_PROG_CXXCPP" | |
76 | "AC_ISC_POSIX" | |
77 | "AC_PROG_F77" | |
78 | "AC_PROG_GCC_TRADITIONAL" | |
79 | "AC_PROG_INSTALL" | |
80 | "AC_PROG_LEX" | |
81 | "AC_PROG_LN_S" | |
82 | "AC_PROG_RANLIB" | |
83 | "AC_PROG_YACC" | |
84 | "AC_CHECK_PROG" | |
85 | "AC_CHECK_PROGS" | |
86 | "AC_PROG_LIBTOOL" | |
87 | ;; Libraries | |
88 | "AC_CHECK_LIB" | |
89 | "AC_PATH_XTRA" | |
90 | ;; Headers | |
91 | "AC_HEADER_STDC" | |
92 | "AC_HEADER_SYS_WAIT" | |
93 | "AC_HEADER_TIME" | |
94 | "AC_HEADERS" | |
95 | ;; Typedefs, structures | |
96 | "AC_TYPE_PID_T" | |
97 | "AC_TYPE_SIGNAL" | |
98 | "AC_TYPE_UID_T" | |
99 | "AC_STRUCT_TM" | |
100 | ;; Compiler characteristics | |
101 | "AC_CHECK_SIZEOF" | |
102 | "AC_C_CONST" | |
103 | ;; Library functions | |
104 | "AC_CHECK_FUNCS" | |
105 | "AC_TRY_LINK" | |
106 | ;; System Services | |
107 | ;; Other | |
108 | "AM_PATH_LISPDIR" | |
109 | "AM_INIT_GUILE_MODULE" | |
110 | ;; AC_OUTPUT is always last | |
111 | "AC_OUTPUT" | |
112 | ) | |
113 | "List of macros in the order that they prefer to occur in. | |
114 | This helps when inserting a macro which doesn't yet exist | |
115 | by positioning it near other macros which may exist. | |
116 | From the autoconf manual: | |
117 | `AC_INIT(FILE)' | |
118 | checks for programs | |
119 | checks for libraries | |
120 | checks for header files | |
121 | checks for typedefs | |
122 | checks for structures | |
123 | checks for compiler characteristics | |
124 | checks for library functions | |
125 | checks for system services | |
126 | `AC_OUTPUT([FILE...])'") | |
127 | ||
128 | (defvar autoconf-multiple-macros | |
129 | '("AC_ARG_ENABLE" | |
130 | "AC_ARG_WITH" | |
131 | "AC_CHECK_PROGS" | |
132 | "AC_CHECK_LIB" | |
133 | "AC_CHECK_SIZEOF" | |
134 | "AC_TRY_LINK" | |
135 | ) | |
136 | "Macros which appear multiple times.") | |
137 | ||
138 | (defvar autoconf-multiple-multiple-macros | |
139 | '("AC_HEADERS" "AC_CHECK_FUNCS") | |
140 | "Macros which appear multiple times, and perform multiple queries.") | |
141 | ||
142 | (defun autoconf-in-macro (macro) | |
143 | "Non-nil if point is in a macro of type MACRO." | |
144 | (save-excursion | |
145 | (beginning-of-line) | |
146 | (looking-at (concat "\\(A[CM]_" macro "\\|" macro "\\)")))) | |
147 | ||
cb85c0d8 | 148 | (defun autoconf-find-last-macro (macro &optional ignore-bol) |
a785b776 | 149 | "Move to the last occurrence of MACRO in FILE, and return that point. |
acc33231 CY |
150 | The last macro is usually the one in which we would like to insert more |
151 | items such as CHECK_HEADERS." | |
cb85c0d8 | 152 | (let ((op (point)) (atbol (if ignore-bol "" "^"))) |
acc33231 | 153 | (goto-char (point-max)) |
cb85c0d8 | 154 | (if (re-search-backward (concat atbol (regexp-quote macro) "\\s-*\\((\\|$\\)") nil t) |
acc33231 | 155 | (progn |
cb85c0d8 | 156 | (unless ignore-bol (beginning-of-line)) |
acc33231 CY |
157 | (point)) |
158 | (goto-char op) | |
159 | nil))) | |
160 | ||
161 | (defun autoconf-parameter-strip (param) | |
a785b776 | 162 | "Strip the parameter PARAM of whitespace and miscellaneous characters." |
cb85c0d8 EL |
163 | ;; force greedy match for \n. |
164 | (when (string-match "\\`\n*\\s-*\\[?\\s-*" param) | |
acc33231 | 165 | (setq param (substring param (match-end 0)))) |
cb85c0d8 | 166 | (when (string-match "\\s-*\\]?\\s-*\\'" param) |
acc33231 | 167 | (setq param (substring param 0 (match-beginning 0)))) |
62a81506 CY |
168 | ;; Look for occurances of backslash newline |
169 | (while (string-match "\\s-*\\\\\\s-*\n\\s-*" param) | |
170 | (setq param (replace-match " " t t param))) | |
acc33231 CY |
171 | param) |
172 | ||
cb85c0d8 | 173 | (defun autoconf-parameters-for-macro (macro &optional ignore-bol ignore-case) |
acc33231 CY |
174 | "Retrieve the parameters to MACRO. |
175 | Returns a list of the arguments passed into MACRO as strings." | |
cb85c0d8 EL |
176 | (let ((case-fold-search ignore-case)) |
177 | (save-excursion | |
178 | (when (autoconf-find-last-macro macro ignore-bol) | |
179 | (forward-sexp 1) | |
180 | (mapcar | |
181 | #'autoconf-parameter-strip | |
182 | (when (looking-at "(") | |
183 | (let* ((start (+ (point) 1)) | |
184 | (end (save-excursion | |
185 | (forward-sexp 1) | |
186 | (- (point) 1))) | |
187 | (ans (buffer-substring-no-properties start end))) | |
188 | (split-string ans "," t)))))))) | |
acc33231 CY |
189 | |
190 | (defun autoconf-position-for-macro (macro) | |
191 | "Position the cursor where a new MACRO could be inserted. | |
192 | This will appear at the BEGINNING of the macro MACRO should appear AFTER. | |
193 | This is to make it compatible with `autoconf-find-last-macro'. | |
194 | Assume that MACRO doesn't appear in the buffer yet, so search | |
195 | the ordering list `autoconf-preferred-macro-order'." | |
196 | ;; Search this list backwards.. heh heh heh | |
e4769531 | 197 | ;; This lets us do a reverse search easily. |
acc33231 CY |
198 | (let ((ml (member macro (reverse autoconf-preferred-macro-order)))) |
199 | (if (not ml) (error "Don't know how to position for %s yet" macro)) | |
200 | (setq ml (cdr ml)) | |
201 | (goto-char (point-max)) | |
202 | (while (and ml (not (autoconf-find-last-macro (car ml)))) | |
203 | (setq ml (cdr ml))) | |
204 | (if (not ml) (error "Could not find context for positioning %s" macro)))) | |
205 | ||
206 | (defun autoconf-insert-macro-at-point (macro &optional param) | |
207 | "Add MACRO at the current point with PARAM." | |
208 | (insert macro) | |
209 | (if param | |
210 | (progn | |
211 | (insert "(" param ")") | |
212 | (if (< (current-column) 3) (insert " dnl"))))) | |
213 | ||
214 | (defun autoconf-insert-new-macro (macro &optional param) | |
215 | "Add a call to MACRO in the current autoconf file. | |
216 | Deals with macro order. See `autoconf-preferred-macro-order' and | |
217 | `autoconf-multi-macros'. | |
218 | Optional argument PARAM is the parameter to pass to the macro as one string." | |
219 | (cond ((member macro autoconf-multiple-macros) | |
220 | ;; This occurs multiple times | |
221 | (or (autoconf-find-last-macro macro) | |
222 | (autoconf-position-for-macro macro)) | |
223 | (forward-sexp 2) | |
224 | (end-of-line) | |
225 | (insert "\n") | |
226 | (autoconf-insert-macro-at-point macro param)) | |
227 | ((member macro autoconf-multiple-multiple-macros) | |
228 | (if (not param) | |
a785b776 | 229 | (error "You must have a parameter for %s" macro)) |
acc33231 CY |
230 | (if (not (autoconf-find-last-macro macro)) |
231 | (progn | |
232 | ;; Doesn't exist yet.... | |
233 | (autoconf-position-for-macro macro) | |
234 | (forward-sexp 2) | |
235 | (end-of-line) | |
236 | (insert "\n") | |
237 | (autoconf-insert-macro-at-point macro param)) | |
238 | ;; Does exist, can we fit onto the current line? | |
239 | (forward-sexp 2) | |
240 | (down-list -1) | |
241 | (if (> (+ (current-column) (length param)) fill-column) | |
242 | (insert " " param) | |
243 | (up-list 1) | |
244 | (end-of-line) | |
245 | (insert "\n") | |
246 | (autoconf-insert-macro-at-point macro param)))) | |
247 | ((autoconf-find-last-macro macro) | |
248 | ;; If it isn't one of the multi's, it's a singleton. | |
249 | ;; If it exists, ignore it. | |
250 | nil) | |
251 | (t | |
252 | (autoconf-position-for-macro macro) | |
253 | (forward-sexp 1) | |
254 | (if (looking-at "\\s-*(") | |
255 | (forward-sexp 1)) | |
256 | (end-of-line) | |
257 | (insert "\n") | |
258 | (autoconf-insert-macro-at-point macro param)))) | |
259 | ||
260 | (defun autoconf-find-query-for-header (header) | |
261 | "Position the cursor where HEADER is queried." | |
262 | (interactive "sHeader: ") | |
263 | (let ((op (point)) | |
264 | (found t)) | |
265 | (goto-char (point-min)) | |
266 | (condition-case nil | |
267 | (while (not | |
268 | (progn | |
269 | (re-search-forward | |
270 | (concat "\\b" (regexp-quote header) "\\b")) | |
271 | (save-excursion | |
272 | (beginning-of-line) | |
273 | (looking-at "AC_CHECK_HEADERS"))))) | |
274 | ;; We depend on the search failing to exit our loop on failure. | |
275 | (error (setq found nil))) | |
276 | (if (not found) (goto-char op)) | |
277 | found)) | |
278 | ||
279 | (defun autoconf-add-query-for-header (header) | |
280 | "Add in HEADER to be queried for in our autoconf file." | |
281 | (interactive "sHeader: ") | |
282 | (or (autoconf-find-query-for-header header) | |
283 | (autoconf-insert-new-macro "AC_CHECK_HEADERS" header))) | |
284 | ||
285 | ||
286 | (defun autoconf-find-query-for-func (func) | |
287 | "Position the cursor where FUNC is queried." | |
288 | (interactive "sFunction: ") | |
289 | (let ((op (point)) | |
290 | (found t)) | |
291 | (goto-char (point-min)) | |
292 | (condition-case nil | |
293 | (while (not | |
294 | (progn | |
295 | (re-search-forward | |
296 | (concat "\\b" (regexp-quote func) "\\b")) | |
297 | (save-excursion | |
298 | (beginning-of-line) | |
299 | (looking-at "AC_CHECK_FUNCS"))))) | |
300 | ;; We depend on the search failing to exit our loop on failure. | |
301 | (error (setq found nil))) | |
302 | (if (not found) (goto-char op)) | |
303 | found)) | |
304 | ||
305 | (defun autoconf-add-query-for-func (func) | |
306 | "Add in FUNC to be queried for in our autoconf file." | |
307 | (interactive "sFunction: ") | |
308 | (or (autoconf-find-query-for-func func) | |
309 | (autoconf-insert-new-macro "AC_CHECK_FUNCS" func))) | |
310 | ||
311 | (defvar autoconf-program-builtin | |
312 | '(("AWK" . "AC_PROG_AWK") | |
313 | ("CC" . "AC_PROG_CC") | |
314 | ("CPP" . "AC_PROG_CPP") | |
315 | ("CXX" . "AC_PROG_CXX") | |
316 | ("CXXCPP" . "AC_PROG_CXXCPP") | |
317 | ("F77" . "AC_PROG_F77") | |
318 | ("GCC_TRADITIONAL" . "AC_PROG_GCC_TRADITIONAL") | |
319 | ("INSTALL" . "AC_PROG_INSTALL") | |
320 | ("LEX" . "AC_PROG_LEX") | |
321 | ("LN_S" . "AC_PROG_LN_S") | |
322 | ("RANLIB" . "AC_PROG_RANLIB") | |
323 | ("YACC" . "AC_PROG_YACC") | |
324 | ) | |
325 | "Association list of PROGRAM variables and their built-in MACRO.") | |
326 | ||
327 | (defun autoconf-find-query-for-program (prog) | |
328 | "Position the cursor where PROG is queried. | |
329 | PROG is the VARIABLE to use in autoconf to identify the program. | |
330 | PROG excludes the _PROG suffix. Thus if PROG were EMACS, then the | |
c4444d16 | 331 | variable in configure.ac would be EMACS_PROG." |
acc33231 CY |
332 | (let ((op (point)) |
333 | (found t) | |
334 | (builtin (assoc prog autoconf-program-builtin))) | |
335 | (goto-char (point-min)) | |
336 | (condition-case nil | |
337 | (re-search-forward | |
338 | (concat "^" | |
339 | (or (cdr-safe builtin) | |
340 | (concat "AC_CHECK_PROG\\s-*(\\s-*" prog "_PROG")) | |
341 | "\\>")) | |
342 | (error (setq found nil))) | |
343 | (if (not found) (goto-char op)) | |
344 | found)) | |
345 | ||
346 | (defun autoconf-add-query-for-program (prog &optional names) | |
347 | "Add in PROG to be queried for in our autoconf file. | |
348 | Optional NAMES is for non-built-in programs, and is the list | |
349 | of possible names." | |
350 | (interactive "sProgram: ") | |
351 | (if (autoconf-find-query-for-program prog) | |
352 | nil | |
353 | (let ((builtin (assoc prog autoconf-program-builtin))) | |
354 | (if builtin | |
355 | (autoconf-insert-new-macro (cdr builtin)) | |
356 | ;; Not built in, try the params item | |
357 | (autoconf-insert-new-macro "AC_CHECK_PROGS" (concat prog "," names)) | |
358 | )))) | |
359 | ||
360 | ;;; Scrappy little changes | |
361 | ;; | |
362 | (defvar autoconf-deleted-text nil | |
363 | "Set to the last bit of text deleted during an edit.") | |
364 | ||
365 | (defvar autoconf-inserted-text nil | |
366 | "Set to the last bit of text inserted during an edit.") | |
367 | ||
368 | (defmacro autoconf-edit-cycle (&rest body) | |
369 | "Start an edit cycle, unsetting the modified flag if there is no change. | |
370 | Optional argument BODY is the code to execute which edits the autoconf file." | |
371 | `(let ((autoconf-deleted-text nil) | |
372 | (autoconf-inserted-text nil) | |
373 | (mod (buffer-modified-p))) | |
374 | ,@body | |
375 | (if (and (not mod) | |
376 | (string= autoconf-deleted-text autoconf-inserted-text)) | |
377 | (set-buffer-modified-p nil)))) | |
378 | ||
62a81506 CY |
379 | (defun autoconf-parameter-count () |
380 | "Return the number of parameters to the function on the current line." | |
381 | (save-excursion | |
382 | (beginning-of-line) | |
383 | (let* ((end-of-cmd | |
384 | (save-excursion | |
385 | (if (re-search-forward "(" (point-at-eol) t) | |
386 | (progn | |
387 | (forward-char -1) | |
388 | (forward-sexp 1) | |
389 | (point)) | |
390 | ;; Else, just return EOL. | |
391 | (point-at-eol)))) | |
392 | (cnt 0)) | |
393 | (save-restriction | |
394 | (narrow-to-region (point-at-bol) end-of-cmd) | |
395 | (condition-case nil | |
396 | (progn | |
397 | (down-list 1) | |
398 | (while (re-search-forward ", ?" end-of-cmd t) | |
399 | (setq cnt (1+ cnt))) | |
400 | (cond ((> cnt 1) | |
401 | ;; If the # is > 1, then there is one fewer , than args. | |
402 | (1+ cnt)) | |
403 | ((not (looking-at "\\s-*)")) | |
404 | ;; If there are 0 args, then we have to see if there is one arg. | |
405 | (1+ cnt)) | |
406 | (t | |
407 | ;; Else, just return the 0. | |
408 | cnt))) | |
409 | (error 0)))))) | |
410 | ||
acc33231 CY |
411 | (defun autoconf-delete-parameter (index) |
412 | "Delete the INDEXth parameter from the macro starting on the current line. | |
413 | Leaves the cursor where a new parameter can be inserted. | |
414 | INDEX starts at 1." | |
415 | (beginning-of-line) | |
416 | (down-list 1) | |
417 | (re-search-forward ", ?" nil nil (1- index)) | |
418 | (let ((end (save-excursion | |
9b026d9f | 419 | (re-search-forward ",\\|)" (point-at-eol)) |
acc33231 CY |
420 | (forward-char -1) |
421 | (point)))) | |
422 | (setq autoconf-deleted-text (buffer-substring (point) end)) | |
423 | (delete-region (point) end))) | |
424 | ||
425 | (defun autoconf-insert (text) | |
426 | "Insert TEXT." | |
427 | (setq autoconf-inserted-text text) | |
428 | (insert text)) | |
429 | ||
430 | (defun autoconf-set-version (version) | |
431 | "Set the version used with automake to VERSION." | |
432 | (if (not (stringp version)) | |
433 | (signal 'wrong-type-argument '(stringp version))) | |
62a81506 CY |
434 | (if (and (autoconf-find-last-macro "AM_INIT_AUTOMAKE") |
435 | (>= (autoconf-parameter-count) 2)) | |
436 | ;; We can edit right here. | |
437 | nil | |
438 | ;; Else, look for AC init instead. | |
439 | (if (not (and (autoconf-find-last-macro "AC_INIT") | |
440 | (>= (autoconf-parameter-count) 2))) | |
441 | (error "Cannot update version"))) | |
442 | ||
443 | ;; Perform the edit. | |
acc33231 CY |
444 | (autoconf-edit-cycle |
445 | (autoconf-delete-parameter 2) | |
62a81506 | 446 | (autoconf-insert (concat "[" version "]")))) |
acc33231 CY |
447 | |
448 | (defun autoconf-set-output (outputlist) | |
449 | "Set the files created in AC_OUTPUT to OUTPUTLIST. | |
450 | OUTPUTLIST is a list of strings representing relative paths | |
451 | to Makefiles, or other files using Autoconf substitution." | |
452 | (if (not (autoconf-find-last-macro "AC_OUTPUT")) | |
453 | (error "Cannot update version") | |
454 | (autoconf-edit-cycle | |
455 | (autoconf-delete-parameter 1) | |
456 | (autoconf-insert (mapconcat (lambda (a) a) outputlist " "))))) | |
457 | ||
458 | (provide 'ede/autoconf-edit) | |
459 | ||
460 | ;;; ede/autoconf-edit.el ends here |