Update copyright year to 2015
[emacs.git] / lisp / cedet / ede / auto.el
blob769bea4ce50e4530e6ef6556fcbb41f677bd6bb0
1 ;;; ede/auto.el --- Autoload features for EDE
3 ;; Copyright (C) 2010-2015 Free Software Foundation, Inc.
5 ;; Author: Eric M. Ludlam <zappo@gnu.org>
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
22 ;;; Commentary:
24 ;; EDE Autoloads are a way to refer to different project types without
25 ;; loading those projects into Emacs.
27 ;; These routines are used to detect a project in a filesystem before
28 ;; handing over control to the usual EDE project system.
30 ;;; Code:
32 (require 'eieio)
34 (declare-function ede-directory-safe-p "ede")
35 (declare-function ede-add-project-to-global-list "ede")
37 (defclass ede-project-autoload-dirmatch ()
38 ((fromconfig :initarg :fromconfig
39 :initform nil
40 :documentation
41 "A config file within which the match pattern lives.")
42 (configregex :initarg :configregex
43 :initform nil
44 :documentation
45 "A regexp to identify the dirmatch pattern.")
46 (configregexidx :initarg :configregexidx
47 :initform nil
48 :documentation
49 "An index into the match-data of `configregex'.")
50 (subdir-only :initarg :subdir-only
51 :initform t
52 :documentation
53 "Non-nil means an exact match to the found directory is a non-match.
54 This implies projects exist only in subdirectories of the configuration path.
55 If `:subdir-only' is nil, then the directory from the configuration file is the project.")
56 (configdatastash :documentation
57 "Save discovered match string.")
59 "Support complex matches for projects that live in named directories.
60 For most cases, a simple string is sufficient. If, however, a project
61 location is varied dependent on other complex criteria, this class
62 can be used to define that match without loading the specific project
63 into memory.")
65 (defmethod ede-dirmatch-installed ((dirmatch ede-project-autoload-dirmatch))
66 "Return non-nil if the tool DIRMATCH might match is installed on the system."
67 (let ((fc (oref dirmatch fromconfig)))
69 (cond
70 ;; If the thing to match is stored in a config file.
71 ((stringp fc)
72 (file-exists-p fc))
74 ;; Add new types of dirmatches here.
76 ;; Error for weird stuff
77 (t (error "Unknown dirmatch type.")))))
80 (defmethod ede-do-dirmatch ((dirmatch ede-project-autoload-dirmatch) file)
81 "Does DIRMATCH match the filename FILE."
82 (let ((fc (oref dirmatch fromconfig)))
84 (cond
85 ;; If the thing to match is stored in a config file.
86 ((stringp fc)
87 (when (file-exists-p fc)
88 (let ((matchstring
89 (if (slot-boundp dirmatch 'configdatastash)
90 (oref dirmatch configdatastash)
91 nil)))
92 (when (and (not matchstring) (not (slot-boundp dirmatch 'configdatastash)))
93 (save-current-buffer
94 (let* ((buff (get-file-buffer fc))
95 (readbuff
96 (let ((find-file-hook nil)) ;; Disable ede from recursing
97 (find-file-noselect fc))))
98 (set-buffer readbuff)
99 (save-excursion
100 (goto-char (point-min))
101 (when (re-search-forward (oref dirmatch configregex) nil t)
102 (setq matchstring
103 (match-string (or (oref dirmatch configregexidx) 0)))))
104 (if (not buff) (kill-buffer readbuff))))
105 (when matchstring
106 ;; If this dirmatch only finds subdirs of matchstring, then
107 ;; force matchstring to be a directory.
108 (when (oref dirmatch subdir-only)
109 (setq matchstring (file-name-as-directory matchstring)))
110 ;; Convert matchstring to a regexp
111 (setq matchstring (concat "^" (regexp-quote matchstring)))
112 ;; Stash it for later.
113 (oset dirmatch configdatastash matchstring))
114 ;; Debug
115 ;;(message "Stashing config data for dirmatch %S as %S" (eieio-object-name dirmatch) matchstring)
117 ;;(message "dirmatch %s against %s" matchstring (expand-file-name file))
118 ;; Match against our discovered string
119 (setq file (file-name-as-directory (expand-file-name file)))
120 (and matchstring (string-match matchstring (expand-file-name file))
121 (or (not (oref dirmatch subdir-only))
122 (not (= (match-end 0) (length file))))
126 ;; Add new matches here
127 ;; ((stringp somenewslot ...)
128 ;; )
130 ;; Error if none others known
132 (error "Unknown dirmatch object match style.")))
135 (declare-function ede-directory-safe-p "ede")
136 (declare-function ede-add-project-to-global-list "ede")
138 (defclass ede-project-autoload ()
139 ((name :initarg :name
140 :documentation "Name of this project type")
141 (file :initarg :file
142 :documentation "The lisp file belonging to this class.")
143 (proj-file :initarg :proj-file
144 :documentation "Name of a project file of this type.")
145 (root-only :initarg :root-only
146 :initform t ;; Default - majority case.
147 :documentation
148 "Non-nil if project detection only finds proj-file @ project root.")
149 (proj-root-dirmatch :initarg :proj-root-dirmatch
150 :initform nil
151 :type (or null string ede-project-autoload-dirmatch)
152 :documentation
153 "To avoid loading a project, check if the directory matches this.
154 Specifying this matcher object will allow EDE to perform a complex
155 check without loading the project.
157 NOTE: If you use dirmatch, you may need to set :root-only to `nil'.
158 While it may be a root based project, all subdirs will happen to return
159 true for the dirmatch, so for scanning purposes, set it to `nil'.")
160 (proj-root :initarg :proj-root
161 :type function
162 :documentation "A function symbol to call for the project root.
163 This function takes no arguments, and returns the current directories
164 root, if available. Leave blank to use the EDE directory walking
165 routine instead.")
166 (initializers :initarg :initializers
167 :initform nil
168 :documentation
169 "Initializers passed to the project object.
170 These are used so there can be multiple types of projects
171 associated with a single object class, based on the initializers used.")
172 (load-type :initarg :load-type
173 :documentation "Fn symbol used to load this project file.")
174 (class-sym :initarg :class-sym
175 :documentation "Symbol representing the project class to use.")
176 (generic-p :initform nil
177 :documentation
178 "Generic projects are added to the project list at the end.
179 The add routine will set this to non-nil so that future non-generic placement will
180 be successful.")
181 (new-p :initarg :new-p
182 :initform t
183 :documentation
184 "Non-nil if this is an option when a user creates a project.")
185 (safe-p :initarg :safe-p
186 :initform t
187 :documentation
188 "Non-nil if the project load files are \"safe\".
189 An unsafe project is one that loads project variables via Emacs
190 Lisp code. A safe project is one that loads project variables by
191 scanning files without loading Lisp code from them.")
193 "Class representing minimal knowledge set to run preliminary EDE functions.
194 When more advanced functionality is needed from a project type, that projects
195 type is required and the load function used.")
197 (defvar ede-project-class-files
198 (list
199 (ede-project-autoload "edeproject-makefile"
200 :name "Make" :file 'ede/proj
201 :proj-file "Project.ede"
202 :root-only nil
203 :load-type 'ede-proj-load
204 :class-sym 'ede-proj-project
205 :safe-p nil)
206 (ede-project-autoload "edeproject-automake"
207 :name "Automake" :file 'ede/proj
208 :proj-file "Project.ede"
209 :root-only nil
210 :initializers '(:makefile-type Makefile.am)
211 :load-type 'ede-proj-load
212 :class-sym 'ede-proj-project
213 :safe-p nil)
214 (ede-project-autoload "automake"
215 :name "automake" :file 'ede/project-am
216 :proj-file "Makefile.am"
217 :root-only nil
218 :load-type 'project-am-load
219 :class-sym 'project-am-makefile
220 :new-p nil
221 :safe-p t)
223 "List of vectors defining how to determine what type of projects exist.")
225 (put 'ede-project-class-files 'risky-local-variable t)
227 (defun ede-add-project-autoload (projauto &optional flag)
228 "Add PROJAUTO, an EDE autoload definition to `ede-project-class-files'.
229 Optional argument FLAG indicates how this autoload should be
230 added. Possible values are:
231 'generic - A generic project type. Keep this at the very end.
232 'unique - A unique project type for a specific project. Keep at the very
233 front of the list so more generic projects don't get priority."
234 ;; First, can we identify PROJAUTO as already in the list? If so, replace.
235 (let ((projlist ede-project-class-files)
236 (projname (eieio-object-name-string projauto)))
237 (while (and projlist (not (string= (eieio-object-name-string (car projlist)) projname)))
238 (setq projlist (cdr projlist)))
240 (if projlist
241 ;; Stick the new one into the old slot.
242 (setcar projlist projauto)
244 ;; Else, see where to insert it.
245 (cond ((and flag (eq flag 'unique))
246 ;; Unique items get stuck right onto the front.
247 (setq ede-project-class-files
248 (cons projauto ede-project-class-files)))
250 ;; Generic Projects go at the very end of the list.
251 ((and flag (eq flag 'generic))
252 (oset projauto generic-p t)
253 (setq ede-project-class-files
254 (append ede-project-class-files
255 (list projauto))))
257 ;; Normal projects go at the end of the list, but
258 ;; before the generic projects.
260 (let ((prev nil)
261 (next ede-project-class-files))
262 (while (and next (not (oref (car next) generic-p)))
263 (setq prev next
264 next (cdr next)))
265 (when (not prev)
266 (error "ede-project-class-files not initialized"))
267 ;; Splice into the list.
268 (setcdr prev (cons projauto next))))))))
270 ;;; Project Autoload Methods
273 ;; New method using detect.el
274 (defmethod ede-auto-detect-in-dir ((this ede-project-autoload) dir)
275 "Return non-nil if THIS project autoload is found in DIR."
276 (let* ((d (file-name-as-directory dir))
277 (pf (oref this proj-file))
278 (f (when (stringp pf) (expand-file-name pf d))))
279 (if f
280 (and f (file-exists-p f))
281 (let ((dirmatch (oref this proj-root-dirmatch)))
282 (cond
283 ((stringp dirmatch)
284 nil) ; <- do something here - maybe obsolete the option?
285 ((ede-project-autoload-dirmatch-p dirmatch)
286 (if (and dirmatch (ede-dirmatch-installed dirmatch))
287 (ede-do-dirmatch dirmatch dir)
288 ;(message "Dirmatch %S not installed." dirmatch)
289 )))))))
291 (defmethod ede-auto-load-project ((this ede-project-autoload) dir)
292 "Load in the project associated with THIS project autoload description.
293 THIS project description should be valid for DIR, where the project will
294 be loaded.
296 NOTE: Do not call this - it should only be called from `ede-load-project-file'."
297 ;; Last line of defense: don't load unsafe projects.
298 (when (not (or (oref this :safe-p)
299 (ede-directory-safe-p dir)))
300 (error "Attempt to load an unsafe project (bug elsewhere in EDE)"))
301 ;; Things are good - so load the project.
302 (let ((o (funcall (oref this load-type) dir)))
303 (when (not o)
304 (error "Project type error: :load-type failed to create a project"))
305 (ede-add-project-to-global-list o)
306 ;; @TODO - Add to hash over at `ede-inode-directory-hash'.
314 ;;; -------- Old Methods
315 ;; See if we can do without them.
317 ;; @FIXME - delete from loaddefs to remove this.
318 (defmethod ede-project-root ((this ede-project-autoload))
319 "If a project knows its root, return it here.
320 Allows for one-project-object-for-a-tree type systems."
321 nil)
323 ;; @FIXME - delete from loaddefs to remove this.
324 (defmethod ede-project-root-directory ((this ede-project-autoload) &optional file)
325 "" nil)
327 (provide 'ede/auto)
329 ;;; ede/auto.el ends here