]> code.delx.au - gnu-emacs/blob - lisp/cedet/ede/linux.el
Remove obsolete leading * from defcustom, defface doc strings.
[gnu-emacs] / lisp / cedet / ede / linux.el
1 ;;; ede/linux.el --- Special project for Linux
2
3 ;; Copyright (C) 2008-2016 Free Software Foundation, Inc.
4
5 ;; Author: Eric M. Ludlam <eric@siege-engine.com>
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 ;; Provide a special project type just for Linux, cause Linux is special.
25 ;;
26 ;; Identifies a Linux project automatically.
27 ;; Speedy ede-expand-filename based on extension.
28 ;; Pre-populates the preprocessor map from lisp.h
29 ;;
30 ;; ToDo :
31 ;; * Add "build" options.
32 ;; * Add texinfo lookup options.
33 ;; * Add website
34
35 (eval-when-compile (require 'cl))
36
37 (require 'ede)
38 (require 'ede/make)
39
40 (declare-function semanticdb-file-table-object "semantic/db")
41 (declare-function semanticdb-needs-refresh-p "semantic/db")
42 (declare-function semanticdb-refresh-table "semantic/db")
43
44 ;;; Code:
45 (defgroup project-linux nil
46 "File and tag browser frame."
47 :group 'tools
48 :group 'ede
49 :version "24.3")
50
51 (defcustom project-linux-build-directory-default 'ask
52 "Build directory."
53 :version "24.4"
54 :group 'project-linux
55 :type '(choice (const :tag "Same as source directory" same)
56 (const :tag "Ask the user" ask)))
57
58 (defcustom project-linux-architecture-default 'ask
59 "Target architecture to assume when not auto-detected."
60 :version "24.4"
61 :group 'project-linux
62 :type '(choice (string :tag "Architecture name")
63 (const :tag "Ask the user" ask)))
64
65
66 (defcustom project-linux-compile-target-command (concat ede-make-command " -k -C %s SUBDIRS=%s")
67 "Default command used to compile a target."
68 :group 'project-linux
69 :type 'string)
70
71 (defcustom project-linux-compile-project-command (concat ede-make-command " -k -C %s")
72 "Default command used to compile a project."
73 :group 'project-linux
74 :type 'string)
75
76 (defun ede-linux-version (dir)
77 "Find the Linux version for the Linux src in DIR."
78 (let ((buff (get-buffer-create " *linux-query*")))
79 (with-current-buffer buff
80 (erase-buffer)
81 (setq default-directory (file-name-as-directory dir))
82 (insert-file-contents "Makefile" nil 0 512)
83 (goto-char (point-min))
84 (let (major minor sub)
85 (re-search-forward "^VERSION *= *\\([0-9.]+\\)")
86 (setq major (match-string 1))
87 (re-search-forward "^PATCHLEVEL *= *\\([0-9.]+\\)")
88 (setq minor (match-string 1))
89 (re-search-forward "^SUBLEVEL *= *\\([0-9.]+\\)")
90 (setq sub (match-string 1))
91 (prog1
92 (concat major "." minor "." sub)
93 (kill-buffer buff)
94 )))))
95
96 (defclass ede-linux-project (ede-project)
97 ((build-directory :initarg :build-directory
98 :type string
99 :documentation "Build directory.")
100 (architecture :initarg :architecture
101 :type string
102 :documentation "Target architecture.")
103 (include-path :initarg :include-path
104 :type list
105 :documentation "Include directories.
106 Contains both common and target architecture-specific directories."))
107 "Project Type for the Linux source code."
108 :method-invocation-order :depth-first)
109
110
111 (defun ede-linux--get-build-directory (dir)
112 "Detect build directory for sources in DIR.
113 If DIR has not been used as a build directory, fall back to
114 `project-linux-build-directory-default'."
115 (or
116 ;; detected build on source directory
117 (and (file-exists-p (expand-file-name ".config" dir)) dir)
118 ;; use configuration
119 (case project-linux-build-directory-default
120 (same dir)
121 (ask (read-directory-name "Select Linux' build directory: " dir)))))
122
123
124 (defun ede-linux--get-archs (dir)
125 "Returns a list of architecture names found in DIR."
126 (let ((archs-dir (expand-file-name "arch" dir))
127 archs)
128 (when (file-directory-p archs-dir)
129 (mapc (lambda (elem)
130 (when (and
131 (not (string= elem "."))
132 (not (string= elem ".."))
133 (not (string= elem "x86_64")) ; has no separate sources
134 (file-directory-p
135 (expand-file-name elem archs-dir)))
136 (add-to-list 'archs elem t)))
137 (directory-files archs-dir)))
138 archs))
139
140
141 (defun ede-linux--detect-architecture (dir)
142 "Try to auto-detect the architecture as configured in DIR.
143 DIR is Linux' build directory. If it cannot be auto-detected,
144 returns `project-linux-architecture-default'."
145 (let ((archs-dir (expand-file-name "arch" dir))
146 (archs (ede-linux--get-archs dir))
147 arch found)
148 (or (and
149 archs
150 ;; Look for /arch/<arch>/include/generated
151 (progn
152 (while (and archs (not found))
153 (setq arch (car archs))
154 (when (file-directory-p
155 (expand-file-name (concat arch "/include/generated")
156 archs-dir))
157 (setq found arch))
158 (setq archs (cdr archs)))
159 found))
160 project-linux-architecture-default)))
161
162 (defun ede-linux--get-architecture (dir bdir)
163 "Try to auto-detect the architecture as configured in BDIR.
164 Uses `ede-linux--detect-architecture' for the auto-detection. If
165 the result is `ask', let the user choose from architectures found
166 in DIR."
167 (let ((arch (ede-linux--detect-architecture bdir)))
168 (case arch
169 (ask
170 (completing-read "Select target architecture: "
171 (ede-linux--get-archs dir)))
172 (t arch))))
173
174
175 (defun ede-linux--include-path (dir bdir arch)
176 "Returns a list with include directories.
177 Returned directories might not exist, since they are not created
178 until Linux is built for the first time."
179 (map 'list
180 (lambda (elem) (format (concat (car elem) "/" (cdr elem)) arch))
181 ;; XXX: taken from the output of "make V=1"
182 (list (cons dir "arch/%s/include")
183 (cons bdir "arch/%s/include/generated")
184 (cons dir "include")
185 (cons bdir "include")
186 (cons dir "arch/%s/include/uapi")
187 (cons bdir "arch/%s/include/generated/uapi")
188 (cons dir "include/uapi")
189 (cons bdir "include/generated/uapi"))))
190
191 ;;;###autoload
192 (defun ede-linux-load (dir &optional _rootproj)
193 "Return an Linux Project object if there is a match.
194 Return nil if there isn't one.
195 Argument DIR is the directory it is created for.
196 ROOTPROJ is nil, since there is only one project."
197 ;; Doesn't already exist, so let's make one.
198 (let* ((bdir (ede-linux--get-build-directory dir))
199 (arch (ede-linux--get-architecture dir bdir))
200 (include-path (ede-linux--include-path dir bdir arch)))
201 (make-instance 'ede-linux-project
202 :name "Linux"
203 :version (ede-linux-version dir)
204 :directory (file-name-as-directory dir)
205 :file (expand-file-name "scripts/ver_linux"
206 dir)
207 :build-directory bdir
208 :architecture arch
209 :include-path include-path)))
210
211 ;;;###autoload
212 (ede-add-project-autoload
213 (make-instance 'ede-project-autoload
214 :name "LINUX ROOT"
215 :file 'ede/linux
216 :proj-file "scripts/ver_linux"
217 :load-type 'ede-linux-load
218 :class-sym 'ede-linux-project
219 :new-p nil
220 :safe-p t)
221 'unique)
222
223 (defclass ede-linux-target-c (ede-target)
224 ()
225 "EDE Linux Project target for C code.
226 All directories need at least one target.")
227
228 (defclass ede-linux-target-misc (ede-target)
229 ()
230 "EDE Linux Project target for Misc files.
231 All directories need at least one target.")
232
233 (cl-defmethod initialize-instance ((this ede-linux-project)
234 &rest _fields)
235 "Make sure the targets slot is bound."
236 (cl-call-next-method)
237 (unless (slot-boundp this 'targets)
238 (oset this :targets nil)))
239
240 ;;; File Stuff
241 ;;
242 (cl-defmethod ede-project-root-directory ((this ede-linux-project)
243 &optional _file)
244 "Return the root for THIS Linux project with file."
245 (ede-up-directory (file-name-directory (oref this file))))
246
247 (cl-defmethod ede-project-root ((this ede-linux-project))
248 "Return my root."
249 this)
250
251 (cl-defmethod ede-find-subproject-for-directory ((proj ede-linux-project)
252 _dir)
253 "Return PROJ, for handling all subdirs below DIR."
254 proj)
255
256 ;;; TARGET MANAGEMENT
257 ;;
258 (defun ede-linux-find-matching-target (class dir targets)
259 "Find a target that is a CLASS and is in DIR in the list of TARGETS."
260 (let ((match nil))
261 (dolist (T targets)
262 (when (and (object-of-class-p T class)
263 (string= (oref T path) dir))
264 (setq match T)
265 ))
266 match))
267
268 (cl-defmethod ede-find-target ((proj ede-linux-project) buffer)
269 "Find an EDE target in PROJ for BUFFER.
270 If one doesn't exist, create a new one for this directory."
271 (let* ((ext (file-name-extension (buffer-file-name buffer)))
272 (cls (cond ((not ext)
273 'ede-linux-target-misc)
274 ((string-match "c\\|h" ext)
275 'ede-linux-target-c)
276 (t 'ede-linux-target-misc)))
277 (targets (oref proj targets))
278 (dir default-directory)
279 (ans (ede-linux-find-matching-target cls dir targets))
280 )
281 (when (not ans)
282 (setq ans (make-instance
283 cls
284 :name (file-name-nondirectory
285 (directory-file-name dir))
286 :path dir
287 :source nil))
288 (object-add-to-list proj :targets ans)
289 )
290 ans))
291
292 ;;; UTILITIES SUPPORT.
293 ;;
294 (cl-defmethod ede-preprocessor-map ((this ede-linux-target-c))
295 "Get the pre-processor map for Linux C code.
296 All files need the macros from lisp.h!"
297 (require 'semantic/db)
298 (let* ((proj (ede-target-parent this))
299 (root (ede-project-root proj))
300 (versionfile (ede-expand-filename root "include/linux/version.h"))
301 (table (when (and versionfile (file-exists-p versionfile))
302 (semanticdb-file-table-object versionfile)))
303 (filemap '( ("__KERNEL__" . "")
304 ))
305 )
306 (when table
307 (when (semanticdb-needs-refresh-p table)
308 (semanticdb-refresh-table table))
309 (setq filemap (append filemap (oref table lexical-table)))
310 )
311 filemap
312 ))
313
314 (defun ede-linux-file-exists-name (name root subdir)
315 "Return a file name if NAME exists under ROOT with SUBDIR in between."
316 (let ((F (expand-file-name name (expand-file-name subdir root))))
317 (when (file-exists-p F) F)))
318
319 (cl-defmethod ede-expand-filename-impl ((proj ede-linux-project) name)
320 "Within this project PROJ, find the file NAME.
321 Knows about how the Linux source tree is organized."
322 (let* ((ext (file-name-extension name))
323 (root (ede-project-root proj))
324 (dir (ede-project-root-directory root))
325 (bdir (oref proj build-directory))
326 (F (cond
327 ((not ext) nil)
328 ((string-match "h" ext)
329 (let ((dirs (oref proj include-path))
330 found)
331 (while (and dirs (not found))
332 (setq found
333 (or (ede-linux-file-exists-name name bdir (car dirs))
334 (ede-linux-file-exists-name name dir (car dirs))))
335 (setq dirs (cdr dirs)))
336 found))
337 ((string-match "txt" ext)
338 (ede-linux-file-exists-name name dir "Documentation"))
339 (t nil))))
340 (or F (cl-call-next-method))))
341
342 ;;; Command Support
343 ;;
344 (cl-defmethod project-compile-project ((proj ede-linux-project)
345 &optional command)
346 "Compile the entire current project.
347 Argument COMMAND is the command to use when compiling."
348 (let* ((dir (ede-project-root-directory proj)))
349
350 (require 'compile)
351 (if (not project-linux-compile-project-command)
352 (setq project-linux-compile-project-command compile-command))
353 (if (not command)
354 (setq command
355 (format
356 project-linux-compile-project-command
357 dir)))
358
359 (compile command)))
360
361 (cl-defmethod project-compile-target ((obj ede-linux-target-c) &optional command)
362 "Compile the current target.
363 Argument COMMAND is the command to use for compiling the target."
364 (let* ((proj (ede-target-parent obj))
365 (root (ede-project-root proj))
366 (dir (ede-project-root-directory root))
367 (subdir (oref obj path)))
368
369 (require 'compile)
370 (if (not project-linux-compile-project-command)
371 (setq project-linux-compile-project-command compile-command))
372 (if (not command)
373 (setq command
374 (format
375 project-linux-compile-target-command
376 dir subdir)))
377
378 (compile command)))
379
380 (cl-defmethod project-rescan ((this ede-linux-project))
381 "Rescan this Linux project from the sources."
382 (let* ((dir (ede-project-root-directory this))
383 (bdir (ede-linux--get-build-directory dir))
384 (arch (ede-linux--get-architecture dir bdir))
385 (inc (ede-linux--include-path dir bdir arch))
386 (ver (ede-linux-version dir)))
387 (oset this version ver)
388 (oset this :build-directory bdir)
389 (oset this :architecture arch)
390 (oset this :include-path inc)
391 ))
392
393 (provide 'ede/linux)
394
395 ;; Local variables:
396 ;; generated-autoload-file: "loaddefs.el"
397 ;; generated-autoload-load-name: "ede/linux"
398 ;; End:
399
400 ;;; ede/linux.el ends here