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