Commit | Line | Data |
---|---|---|
acc33231 CY |
1 | ;;; ede/autoconf-edit.el --- Keymap for autoconf |
2 | ||
95df8112 | 3 | ;; Copyright (C) 1998-2000, 2009-2011 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) | |
34 | "Initialize a new configure.in in ROOTDIR for PROGRAM using TESTFILE. | |
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 CY |
167 | (setq param (substring param 0 (match-beginning 0)))) |
168 | param) | |
169 | ||
cb85c0d8 | 170 | (defun autoconf-parameters-for-macro (macro &optional ignore-bol ignore-case) |
acc33231 CY |
171 | "Retrieve the parameters to MACRO. |
172 | Returns a list of the arguments passed into MACRO as strings." | |
cb85c0d8 EL |
173 | (let ((case-fold-search ignore-case)) |
174 | (save-excursion | |
175 | (when (autoconf-find-last-macro macro ignore-bol) | |
176 | (forward-sexp 1) | |
177 | (mapcar | |
178 | #'autoconf-parameter-strip | |
179 | (when (looking-at "(") | |
180 | (let* ((start (+ (point) 1)) | |
181 | (end (save-excursion | |
182 | (forward-sexp 1) | |
183 | (- (point) 1))) | |
184 | (ans (buffer-substring-no-properties start end))) | |
185 | (split-string ans "," t)))))))) | |
acc33231 CY |
186 | |
187 | (defun autoconf-position-for-macro (macro) | |
188 | "Position the cursor where a new MACRO could be inserted. | |
189 | This will appear at the BEGINNING of the macro MACRO should appear AFTER. | |
190 | This is to make it compatible with `autoconf-find-last-macro'. | |
191 | Assume that MACRO doesn't appear in the buffer yet, so search | |
192 | the ordering list `autoconf-preferred-macro-order'." | |
193 | ;; Search this list backwards.. heh heh heh | |
194 | ;; This lets us do a reverse search easilly. | |
195 | (let ((ml (member macro (reverse autoconf-preferred-macro-order)))) | |
196 | (if (not ml) (error "Don't know how to position for %s yet" macro)) | |
197 | (setq ml (cdr ml)) | |
198 | (goto-char (point-max)) | |
199 | (while (and ml (not (autoconf-find-last-macro (car ml)))) | |
200 | (setq ml (cdr ml))) | |
201 | (if (not ml) (error "Could not find context for positioning %s" macro)))) | |
202 | ||
203 | (defun autoconf-insert-macro-at-point (macro &optional param) | |
204 | "Add MACRO at the current point with PARAM." | |
205 | (insert macro) | |
206 | (if param | |
207 | (progn | |
208 | (insert "(" param ")") | |
209 | (if (< (current-column) 3) (insert " dnl"))))) | |
210 | ||
211 | (defun autoconf-insert-new-macro (macro &optional param) | |
212 | "Add a call to MACRO in the current autoconf file. | |
213 | Deals with macro order. See `autoconf-preferred-macro-order' and | |
214 | `autoconf-multi-macros'. | |
215 | Optional argument PARAM is the parameter to pass to the macro as one string." | |
216 | (cond ((member macro autoconf-multiple-macros) | |
217 | ;; This occurs multiple times | |
218 | (or (autoconf-find-last-macro macro) | |
219 | (autoconf-position-for-macro macro)) | |
220 | (forward-sexp 2) | |
221 | (end-of-line) | |
222 | (insert "\n") | |
223 | (autoconf-insert-macro-at-point macro param)) | |
224 | ((member macro autoconf-multiple-multiple-macros) | |
225 | (if (not param) | |
a785b776 | 226 | (error "You must have a parameter for %s" macro)) |
acc33231 CY |
227 | (if (not (autoconf-find-last-macro macro)) |
228 | (progn | |
229 | ;; Doesn't exist yet.... | |
230 | (autoconf-position-for-macro macro) | |
231 | (forward-sexp 2) | |
232 | (end-of-line) | |
233 | (insert "\n") | |
234 | (autoconf-insert-macro-at-point macro param)) | |
235 | ;; Does exist, can we fit onto the current line? | |
236 | (forward-sexp 2) | |
237 | (down-list -1) | |
238 | (if (> (+ (current-column) (length param)) fill-column) | |
239 | (insert " " param) | |
240 | (up-list 1) | |
241 | (end-of-line) | |
242 | (insert "\n") | |
243 | (autoconf-insert-macro-at-point macro param)))) | |
244 | ((autoconf-find-last-macro macro) | |
245 | ;; If it isn't one of the multi's, it's a singleton. | |
246 | ;; If it exists, ignore it. | |
247 | nil) | |
248 | (t | |
249 | (autoconf-position-for-macro macro) | |
250 | (forward-sexp 1) | |
251 | (if (looking-at "\\s-*(") | |
252 | (forward-sexp 1)) | |
253 | (end-of-line) | |
254 | (insert "\n") | |
255 | (autoconf-insert-macro-at-point macro param)))) | |
256 | ||
257 | (defun autoconf-find-query-for-header (header) | |
258 | "Position the cursor where HEADER is queried." | |
259 | (interactive "sHeader: ") | |
260 | (let ((op (point)) | |
261 | (found t)) | |
262 | (goto-char (point-min)) | |
263 | (condition-case nil | |
264 | (while (not | |
265 | (progn | |
266 | (re-search-forward | |
267 | (concat "\\b" (regexp-quote header) "\\b")) | |
268 | (save-excursion | |
269 | (beginning-of-line) | |
270 | (looking-at "AC_CHECK_HEADERS"))))) | |
271 | ;; We depend on the search failing to exit our loop on failure. | |
272 | (error (setq found nil))) | |
273 | (if (not found) (goto-char op)) | |
274 | found)) | |
275 | ||
276 | (defun autoconf-add-query-for-header (header) | |
277 | "Add in HEADER to be queried for in our autoconf file." | |
278 | (interactive "sHeader: ") | |
279 | (or (autoconf-find-query-for-header header) | |
280 | (autoconf-insert-new-macro "AC_CHECK_HEADERS" header))) | |
281 | ||
282 | ||
283 | (defun autoconf-find-query-for-func (func) | |
284 | "Position the cursor where FUNC is queried." | |
285 | (interactive "sFunction: ") | |
286 | (let ((op (point)) | |
287 | (found t)) | |
288 | (goto-char (point-min)) | |
289 | (condition-case nil | |
290 | (while (not | |
291 | (progn | |
292 | (re-search-forward | |
293 | (concat "\\b" (regexp-quote func) "\\b")) | |
294 | (save-excursion | |
295 | (beginning-of-line) | |
296 | (looking-at "AC_CHECK_FUNCS"))))) | |
297 | ;; We depend on the search failing to exit our loop on failure. | |
298 | (error (setq found nil))) | |
299 | (if (not found) (goto-char op)) | |
300 | found)) | |
301 | ||
302 | (defun autoconf-add-query-for-func (func) | |
303 | "Add in FUNC to be queried for in our autoconf file." | |
304 | (interactive "sFunction: ") | |
305 | (or (autoconf-find-query-for-func func) | |
306 | (autoconf-insert-new-macro "AC_CHECK_FUNCS" func))) | |
307 | ||
308 | (defvar autoconf-program-builtin | |
309 | '(("AWK" . "AC_PROG_AWK") | |
310 | ("CC" . "AC_PROG_CC") | |
311 | ("CPP" . "AC_PROG_CPP") | |
312 | ("CXX" . "AC_PROG_CXX") | |
313 | ("CXXCPP" . "AC_PROG_CXXCPP") | |
314 | ("F77" . "AC_PROG_F77") | |
315 | ("GCC_TRADITIONAL" . "AC_PROG_GCC_TRADITIONAL") | |
316 | ("INSTALL" . "AC_PROG_INSTALL") | |
317 | ("LEX" . "AC_PROG_LEX") | |
318 | ("LN_S" . "AC_PROG_LN_S") | |
319 | ("RANLIB" . "AC_PROG_RANLIB") | |
320 | ("YACC" . "AC_PROG_YACC") | |
321 | ) | |
322 | "Association list of PROGRAM variables and their built-in MACRO.") | |
323 | ||
324 | (defun autoconf-find-query-for-program (prog) | |
325 | "Position the cursor where PROG is queried. | |
326 | PROG is the VARIABLE to use in autoconf to identify the program. | |
327 | PROG excludes the _PROG suffix. Thus if PROG were EMACS, then the | |
328 | variable in configure.in would be EMACS_PROG." | |
329 | (let ((op (point)) | |
330 | (found t) | |
331 | (builtin (assoc prog autoconf-program-builtin))) | |
332 | (goto-char (point-min)) | |
333 | (condition-case nil | |
334 | (re-search-forward | |
335 | (concat "^" | |
336 | (or (cdr-safe builtin) | |
337 | (concat "AC_CHECK_PROG\\s-*(\\s-*" prog "_PROG")) | |
338 | "\\>")) | |
339 | (error (setq found nil))) | |
340 | (if (not found) (goto-char op)) | |
341 | found)) | |
342 | ||
343 | (defun autoconf-add-query-for-program (prog &optional names) | |
344 | "Add in PROG to be queried for in our autoconf file. | |
345 | Optional NAMES is for non-built-in programs, and is the list | |
346 | of possible names." | |
347 | (interactive "sProgram: ") | |
348 | (if (autoconf-find-query-for-program prog) | |
349 | nil | |
350 | (let ((builtin (assoc prog autoconf-program-builtin))) | |
351 | (if builtin | |
352 | (autoconf-insert-new-macro (cdr builtin)) | |
353 | ;; Not built in, try the params item | |
354 | (autoconf-insert-new-macro "AC_CHECK_PROGS" (concat prog "," names)) | |
355 | )))) | |
356 | ||
357 | ;;; Scrappy little changes | |
358 | ;; | |
359 | (defvar autoconf-deleted-text nil | |
360 | "Set to the last bit of text deleted during an edit.") | |
361 | ||
362 | (defvar autoconf-inserted-text nil | |
363 | "Set to the last bit of text inserted during an edit.") | |
364 | ||
365 | (defmacro autoconf-edit-cycle (&rest body) | |
366 | "Start an edit cycle, unsetting the modified flag if there is no change. | |
367 | Optional argument BODY is the code to execute which edits the autoconf file." | |
368 | `(let ((autoconf-deleted-text nil) | |
369 | (autoconf-inserted-text nil) | |
370 | (mod (buffer-modified-p))) | |
371 | ,@body | |
372 | (if (and (not mod) | |
373 | (string= autoconf-deleted-text autoconf-inserted-text)) | |
374 | (set-buffer-modified-p nil)))) | |
375 | ||
376 | (defun autoconf-delete-parameter (index) | |
377 | "Delete the INDEXth parameter from the macro starting on the current line. | |
378 | Leaves the cursor where a new parameter can be inserted. | |
379 | INDEX starts at 1." | |
380 | (beginning-of-line) | |
381 | (down-list 1) | |
382 | (re-search-forward ", ?" nil nil (1- index)) | |
383 | (let ((end (save-excursion | |
9b026d9f | 384 | (re-search-forward ",\\|)" (point-at-eol)) |
acc33231 CY |
385 | (forward-char -1) |
386 | (point)))) | |
387 | (setq autoconf-deleted-text (buffer-substring (point) end)) | |
388 | (delete-region (point) end))) | |
389 | ||
390 | (defun autoconf-insert (text) | |
391 | "Insert TEXT." | |
392 | (setq autoconf-inserted-text text) | |
393 | (insert text)) | |
394 | ||
395 | (defun autoconf-set-version (version) | |
396 | "Set the version used with automake to VERSION." | |
397 | (if (not (stringp version)) | |
398 | (signal 'wrong-type-argument '(stringp version))) | |
399 | (if (not (autoconf-find-last-macro "AM_INIT_AUTOMAKE")) | |
400 | (error "Cannot update version") | |
401 | ;; Move to correct position. | |
402 | (autoconf-edit-cycle | |
403 | (autoconf-delete-parameter 2) | |
404 | (autoconf-insert version)))) | |
405 | ||
406 | (defun autoconf-set-output (outputlist) | |
407 | "Set the files created in AC_OUTPUT to OUTPUTLIST. | |
408 | OUTPUTLIST is a list of strings representing relative paths | |
409 | to Makefiles, or other files using Autoconf substitution." | |
410 | (if (not (autoconf-find-last-macro "AC_OUTPUT")) | |
411 | (error "Cannot update version") | |
412 | (autoconf-edit-cycle | |
413 | (autoconf-delete-parameter 1) | |
414 | (autoconf-insert (mapconcat (lambda (a) a) outputlist " "))))) | |
415 | ||
416 | (provide 'ede/autoconf-edit) | |
417 | ||
418 | ;;; ede/autoconf-edit.el ends here |