Commit | Line | Data |
---|---|---|
acc33231 CY |
1 | ;;; ede-proj-elisp.el --- EDE Generic Project Emacs Lisp support |
2 | ||
a785b776 | 3 | ;; Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, |
5df4f04c | 4 | ;; 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. |
acc33231 CY |
5 | |
6 | ;; Author: Eric M. Ludlam <zappo@gnu.org> | |
7 | ;; Keywords: project, make | |
8 | ||
9 | ;; This file is part of GNU Emacs. | |
10 | ||
11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
12 | ;; it under the terms of the GNU General Public License as published by | |
13 | ;; the Free Software Foundation, either version 3 of the License, or | |
14 | ;; (at your option) any later version. | |
15 | ||
16 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | ;; GNU General Public License for more details. | |
20 | ||
21 | ;; You should have received a copy of the GNU General Public License | |
22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
23 | ||
24 | ;;; Commentary: | |
25 | ;; | |
b90caf50 | 26 | ;; Handle Emacs Lisp in an EDE Project file. |
acc33231 CY |
27 | |
28 | (require 'ede/proj) | |
29 | (require 'ede/pmake) | |
30 | (require 'ede/pconf) | |
31 | ||
a2095e2e CY |
32 | (autoload 'semantic-ede-proj-target-grammar "semantic/ede-grammar") |
33 | ||
acc33231 CY |
34 | ;;; Code: |
35 | (defclass ede-proj-target-elisp (ede-proj-target-makefile) | |
36 | ((menu :initform nil) | |
37 | (keybindings :initform nil) | |
38 | (phony :initform t) | |
cb85c0d8 EL |
39 | (sourcetype :initform '(ede-source-emacs)) |
40 | (availablecompilers :initform '(ede-emacs-compiler ede-xemacs-compiler)) | |
acc33231 CY |
41 | (aux-packages :initarg :aux-packages |
42 | :initform nil | |
43 | :type list | |
44 | :custom (repeat string) | |
45 | :documentation "Additional packages needed. | |
46 | There should only be one toplevel package per auxiliary tool needed. | |
47 | These packages location is found, and added to the compile time | |
48 | load path." | |
49 | )) | |
50 | "This target consists of a group of lisp files. | |
51 | A lisp target may be one general program with many separate lisp files in it.") | |
52 | ||
53 | (defvar ede-source-emacs | |
54 | (ede-sourcecode "ede-emacs-source" | |
55 | :name "Emacs Lisp" | |
56 | :sourcepattern "\\.el$" | |
57 | :garbagepattern '("*.elc")) | |
58 | "Emacs Lisp source code definition.") | |
59 | ||
60 | (defvar ede-emacs-compiler | |
61 | (ede-compiler | |
62 | "ede-emacs-compiler" | |
63 | :name "emacs" | |
64 | :variables '(("EMACS" . "emacs") | |
65 | ("EMACSFLAGS" . "-batch --no-site-file")) | |
66 | :commands | |
67 | '("@echo \"(add-to-list 'load-path nil)\" > $@-compile-script" | |
68 | "for loadpath in . ${LOADPATH}; do \\" | |
69 | " echo \"(add-to-list 'load-path \\\"$$loadpath\\\")\" >> $@-compile-script; \\" | |
70 | "done;" | |
71 | "@echo \"(setq debug-on-error t)\" >> $@-compile-script" | |
72 | "\"$(EMACS)\" $(EMACSFLAGS) -l $@-compile-script -f batch-byte-compile $^" | |
73 | ) | |
74 | :autoconf '("AM_PATH_LISPDIR") | |
75 | :sourcetype '(ede-source-emacs) | |
76 | ; :objectextention ".elc" | |
77 | ) | |
78 | "Compile Emacs Lisp programs.") | |
79 | ||
80 | (defvar ede-xemacs-compiler | |
81 | (clone ede-emacs-compiler "ede-xemacs-compiler" | |
82 | :name "xemacs" | |
83 | :variables '(("EMACS" . "xemacs"))) | |
84 | "Compile Emacs Lisp programs with XEmacs.") | |
85 | ||
86 | ;;; Claiming files | |
87 | (defmethod ede-buffer-mine ((this ede-proj-target-elisp) buffer) | |
88 | "Return t if object THIS lays claim to the file in BUFFER. | |
89 | Lays claim to all .elc files that match .el files in this target." | |
90 | (if (string-match "\\.elc$" (buffer-file-name buffer)) | |
91 | (let ((fname | |
92 | (concat | |
93 | (file-name-sans-extension (buffer-file-name buffer)) | |
94 | ".el") | |
95 | )) | |
96 | ;; Is this in our list. | |
97 | (member fname (oref this auxsource)) | |
98 | ) | |
99 | (call-next-method) ; The usual thing. | |
100 | )) | |
101 | ||
102 | ;;; Emacs Lisp Compiler | |
103 | ;;; Emacs Lisp Target | |
104 | (defun ede-proj-elisp-packages-to-loadpath (packages) | |
105 | "Convert a list of PACKAGES, to a list of load path." | |
106 | (let ((paths nil) | |
107 | (ldir nil)) | |
108 | (while packages | |
109 | (or (setq ldir (locate-library (car packages))) | |
110 | (error "Cannot find package %s" (car packages))) | |
111 | (let* ((fnd (file-name-directory ldir)) | |
112 | (rel (file-relative-name fnd)) | |
113 | (full nil) | |
114 | ) | |
115 | ;; Make sure the relative name isn't to far off | |
116 | (when (string-match "^\\.\\./\\.\\./\\.\\./\\.\\." rel) | |
117 | (setq full fnd)) | |
118 | ;; Do the setup. | |
119 | (setq paths (cons (or full rel) paths) | |
120 | packages (cdr packages)))) | |
121 | paths)) | |
122 | ||
123 | (defmethod project-compile-target ((obj ede-proj-target-elisp)) | |
124 | "Compile all sources in a Lisp target OBJ. | |
125 | Bonus: Return a cons cell: (COMPILED . UPTODATE)." | |
126 | (let* ((proj (ede-target-parent obj)) | |
127 | (dir (oref proj directory)) | |
128 | (comp 0) | |
129 | (utd 0)) | |
130 | (mapc (lambda (src) | |
131 | (let* ((fsrc (expand-file-name src dir)) | |
132 | (elc (concat (file-name-sans-extension fsrc) ".elc")) | |
133 | ) | |
134 | (if (or (not (file-exists-p elc)) | |
135 | (file-newer-than-file-p fsrc elc)) | |
136 | (progn | |
137 | (setq comp (1+ comp)) | |
138 | (byte-compile-file fsrc)) | |
139 | (setq utd (1+ utd))))) | |
140 | (oref obj source)) | |
141 | (message "All Emacs Lisp sources are up to date in %s" (object-name obj)) | |
142 | (cons comp utd) | |
143 | )) | |
144 | ||
145 | (defmethod ede-update-version-in-source ((this ede-proj-target-elisp) version) | |
146 | "In a Lisp file, updated a version string for THIS to VERSION. | |
147 | There are standards in Elisp files specifying how the version string | |
148 | is found, such as a `-version' variable, or the standard header." | |
149 | (if (and (slot-boundp this 'versionsource) | |
150 | (oref this versionsource)) | |
151 | (let ((vs (oref this versionsource)) | |
152 | (match nil)) | |
153 | (while vs | |
0816d744 SM |
154 | (with-current-buffer (find-file-noselect |
155 | (ede-expand-filename this (car vs))) | |
acc33231 CY |
156 | (goto-char (point-min)) |
157 | (let ((case-fold-search t)) | |
158 | (if (re-search-forward "-version\\s-+\"\\([^\"]+\\)\"" nil t) | |
159 | (progn | |
160 | (setq match t) | |
161 | (delete-region (match-beginning 1) | |
162 | (match-end 1)) | |
163 | (goto-char (match-beginning 1)) | |
164 | (insert version))))) | |
165 | (setq vs (cdr vs))) | |
166 | (if (not match) (call-next-method))))) | |
167 | ||
168 | ||
169 | ;;; Makefile generation functions | |
170 | ;; | |
171 | (defmethod ede-proj-makefile-sourcevar ((this ede-proj-target-elisp)) | |
172 | "Return the variable name for THIS's sources." | |
173 | (cond ((ede-proj-automake-p) '("lisp_LISP" . share)) | |
174 | (t (concat (ede-pmake-varname this) "_LISP")))) | |
175 | ||
176 | (defun ede-proj-makefile-insert-loadpath-items (items) | |
177 | "Insert a sequence of ITEMS into the Makefile LOADPATH variable." | |
178 | (when items | |
179 | (ede-pmake-insert-variable-shared "LOADPATH" | |
180 | (let ((begin (save-excursion (re-search-backward "\\s-*=")))) | |
181 | (while items | |
182 | (when (not (save-excursion | |
183 | (re-search-backward | |
184 | (concat "\\s-" (regexp-quote (car items)) "[ \n\t\\]") | |
185 | begin t))) | |
186 | (insert " " (car items))) | |
187 | (setq items (cdr items))))) | |
188 | )) | |
189 | ||
190 | (defmethod ede-proj-makefile-insert-variables :AFTER ((this ede-proj-target-elisp)) | |
191 | "Insert variables needed by target THIS." | |
192 | (let ((newitems (if (oref this aux-packages) | |
193 | (ede-proj-elisp-packages-to-loadpath | |
194 | (oref this aux-packages)))) | |
195 | ) | |
196 | (ede-proj-makefile-insert-loadpath-items newitems))) | |
197 | ||
198 | (defun ede-proj-elisp-add-path (path) | |
199 | "Add path PATH into the file if it isn't already there." | |
200 | (goto-char (point-min)) | |
201 | (if (re-search-forward (concat "(cons \\\"" | |
202 | (regexp-quote path)) | |
203 | nil t) | |
204 | nil;; We have it already | |
205 | (if (re-search-forward "(cons nil" nil t) | |
206 | (progn | |
207 | ;; insert stuff here | |
208 | (end-of-line) | |
209 | (insert "\n" | |
210 | " echo \"(setq load-path (cons \\\"" | |
211 | path | |
212 | "\\\" load-path))\" >> script") | |
213 | ) | |
214 | (error "Don't know how to update load path")))) | |
215 | ||
216 | (defmethod ede-proj-tweak-autoconf ((this ede-proj-target-elisp)) | |
217 | "Tweak the configure file (current buffer) to accomodate THIS." | |
218 | (call-next-method) | |
219 | ;; Ok, now we have to tweak the autoconf provided `elisp-comp' program. | |
220 | (let ((ec (ede-expand-filename this "elisp-comp" 'newfile))) | |
221 | (if (or (not ec) (not (file-exists-p ec))) | |
222 | (message "No elisp-comp file. There may be compile errors? Rerun a second time.") | |
223 | (save-excursion | |
224 | (if (file-symlink-p ec) | |
225 | (progn | |
226 | ;; Desymlinkafy | |
227 | (rename-file ec (concat ec ".tmp")) | |
228 | (copy-file (concat ec ".tmp") ec) | |
229 | (delete-file (concat ec ".tmp")))) | |
230 | (set-buffer (find-file-noselect ec t)) | |
231 | (ede-proj-elisp-add-path "..") | |
232 | (let ((paths (ede-proj-elisp-packages-to-loadpath | |
233 | (oref this aux-packages)))) | |
234 | ;; Add in the current list of paths | |
235 | (while paths | |
236 | (ede-proj-elisp-add-path (car paths)) | |
237 | (setq paths (cdr paths)))) | |
238 | (save-buffer)) ))) | |
239 | ||
240 | (defmethod ede-proj-flush-autoconf ((this ede-proj-target-elisp)) | |
241 | "Flush the configure file (current buffer) to accomodate THIS." | |
242 | ;; Remove crufty old paths from elisp-compile | |
243 | (let ((ec (ede-expand-filename this "elisp-comp" 'newfile)) | |
244 | ) | |
245 | (if (and ec (file-exists-p ec)) | |
0816d744 | 246 | (with-current-buffer (find-file-noselect ec t) |
acc33231 CY |
247 | (goto-char (point-min)) |
248 | (while (re-search-forward "(cons \\([^ ]+\\) load-path)" | |
249 | nil t) | |
250 | (let ((path (match-string 1))) | |
251 | (if (string= path "nil") | |
252 | nil | |
253 | (delete-region (save-excursion (beginning-of-line) (point)) | |
254 | (save-excursion (end-of-line) | |
255 | (forward-char 1) | |
256 | (point)))))))))) | |
257 | ||
258 | ;;; | |
259 | ;; Autoload generators | |
260 | ;; | |
261 | (defclass ede-proj-target-elisp-autoloads (ede-proj-target-elisp) | |
cb85c0d8 | 262 | ((availablecompilers :initform '(ede-emacs-cedet-autogen-compiler)) |
acc33231 CY |
263 | (aux-packages :initform ("cedet-autogen")) |
264 | (phony :initform t) | |
265 | (autoload-file :initarg :autoload-file | |
266 | :initform "loaddefs.el" | |
267 | :type string | |
268 | :custom string | |
269 | :documentation "The file that autoload definitions are placed in. | |
270 | There should be one load defs file for a given package. The load defs are created | |
271 | for all Emacs Lisp sources that exist in the directory of the created target.") | |
272 | (autoload-dirs :initarg :autoload-dirs | |
273 | :initform nil | |
274 | :type list | |
275 | :custom (repeat string) | |
276 | :documentation "The directories to scan for autoload definitions. | |
277 | If nil defaults to the current directory.") | |
278 | ) | |
279 | "Target that builds an autoload file. | |
280 | Files do not need to be added to this target.") | |
281 | ||
282 | ||
283 | ;;; Claiming files | |
284 | (defmethod ede-buffer-mine ((this ede-proj-target-elisp-autoloads) buffer) | |
285 | "Return t if object THIS lays claim to the file in BUFFER. | |
286 | Lays claim to all .elc files that match .el files in this target." | |
287 | (if (string-match | |
288 | (concat (regexp-quote (oref this autoload-file)) "$") | |
289 | (buffer-file-name buffer)) | |
290 | t | |
291 | (call-next-method) ; The usual thing. | |
292 | )) | |
293 | ||
294 | ;; Compilers | |
295 | (defvar ede-emacs-cedet-autogen-compiler | |
296 | (ede-compiler | |
297 | "ede-emacs-autogen-compiler" | |
298 | :name "emacs" | |
299 | :variables '(("EMACS" . "emacs")) | |
300 | :commands | |
301 | '("@echo \"(add-to-list 'load-path nil)\" > $@-compile-script" | |
302 | "for loadpath in . ${LOADPATH}; do \\" | |
303 | " echo \"(add-to-list 'load-path \\\"$$loadpath\\\")\" >> $@-compile-script; \\" | |
304 | "done;" | |
305 | "@echo \"(require 'cedet-autogen)\" >> $@-compile-script" | |
306 | "\"$(EMACS)\" -batch --no-site-file -l $@-compile-script -f cedet-batch-update-autoloads $(LOADDEFS) $(LOADDIRS)" | |
307 | ) | |
308 | :sourcetype '(ede-source-emacs) | |
309 | ) | |
310 | "Build an autoloads file.") | |
311 | ||
312 | (defmethod ede-proj-compilers ((obj ede-proj-target-elisp-autoloads)) | |
313 | "List of compilers being used by OBJ. | |
314 | If the `compiler' slot is empty, get the car of the compilers list." | |
315 | (let ((comp (oref obj compiler))) | |
316 | (if comp | |
317 | (if (listp comp) | |
318 | (setq comp (mapcar 'symbol-value comp)) | |
319 | (setq comp (list (symbol-value comp)))) | |
320 | ;; Get the first element from our list of compilers. | |
321 | (let ((avail (mapcar 'symbol-value (oref obj availablecompilers)))) | |
322 | (setq comp (list (car avail))))) | |
323 | comp)) | |
324 | ||
325 | (defmethod ede-proj-makefile-insert-source-variables ((this ede-proj-target-elisp-autoloads) | |
326 | &optional | |
327 | moresource) | |
328 | "Insert the source variables needed by THIS. | |
329 | Optional argument MORESOURCE is a list of additional sources to add to the | |
330 | sources variable." | |
331 | nil) | |
332 | ||
333 | (defmethod ede-proj-makefile-sourcevar ((this ede-proj-target-elisp-autoloads)) | |
334 | "Return the variable name for THIS's sources." | |
335 | nil) ; "LOADDEFS") | |
336 | ||
337 | (defmethod ede-proj-makefile-dependencies ((this ede-proj-target-elisp-autoloads)) | |
338 | "Return a string representing the dependencies for THIS. | |
339 | Always return an empty string for an autoloads generator." | |
340 | "") | |
341 | ||
342 | (defmethod ede-proj-makefile-insert-variables :AFTER ((this ede-proj-target-elisp-autoloads)) | |
343 | "Insert variables needed by target THIS." | |
344 | (ede-pmake-insert-variable-shared "LOADDEFS" | |
345 | (insert (oref this autoload-file))) | |
346 | (ede-pmake-insert-variable-shared "LOADDIRS" | |
347 | (insert (mapconcat 'identity | |
348 | (or (oref this autoload-dirs) '(".")) | |
349 | " "))) | |
350 | ) | |
351 | ||
352 | (defmethod project-compile-target ((obj ede-proj-target-elisp-autoloads)) | |
353 | "Create or update the autoload target." | |
354 | (require 'cedet-autogen) | |
355 | (let ((default-directory (ede-expand-filename obj "."))) | |
356 | (apply 'cedet-update-autoloads | |
357 | (oref obj autoload-file) | |
358 | (oref obj autoload-dirs)) | |
359 | )) | |
360 | ||
361 | (defmethod ede-update-version-in-source ((this ede-proj-target-elisp-autoloads) version) | |
362 | "In a Lisp file, updated a version string for THIS to VERSION. | |
363 | There are standards in Elisp files specifying how the version string | |
364 | is found, such as a `-version' variable, or the standard header." | |
365 | nil) | |
366 | ||
367 | (defmethod ede-proj-makefile-insert-dist-dependencies ((this ede-proj-target-elisp-autoloads)) | |
368 | "Insert any symbols that the DIST rule should depend on. | |
369 | Emacs Lisp autoload files ship the generated .el files. | |
370 | Argument THIS is the target which needs to insert an info file." | |
371 | ;; In some cases, this is ONLY the index file. That should generally | |
372 | ;; be ok. | |
373 | (insert " " (ede-proj-makefile-target-name this)) | |
374 | ) | |
375 | ||
376 | (defmethod ede-proj-makefile-insert-dist-filepatterns ((this ede-proj-target-elisp-autoloads)) | |
377 | "Insert any symbols that the DIST rule should distribute. | |
378 | Emacs Lisp autoload files ship the generated .el files. | |
379 | Argument THIS is the target which needs to insert an info file." | |
380 | (insert " " (oref this autoload-file)) | |
381 | ) | |
382 | ||
383 | (defmethod ede-proj-tweak-autoconf ((this ede-proj-target-elisp-autoloads)) | |
384 | "Tweak the configure file (current buffer) to accomodate THIS." | |
a785b776 | 385 | (error "Autoloads not supported in autoconf yet")) |
acc33231 CY |
386 | |
387 | (defmethod ede-proj-flush-autoconf ((this ede-proj-target-elisp-autoloads)) | |
388 | "Flush the configure file (current buffer) to accomodate THIS." | |
389 | nil) | |
390 | ||
391 | (provide 'ede/proj-elisp) | |
392 | ||
3999968a | 393 | ;; arch-tag: 3802c94b-d04d-4ecf-9bab-b29ed6e77588 |
acc33231 | 394 | ;;; ede/proj-elisp.el ends here |