Update copyright year to 2014 by running admin/update-copyright.
[emacs.git] / lisp / cedet / ede / linux.el
blob4e8a06ef1105974b599d84903bb2d52ea3f3d544
1 ;;; ede/linux.el --- Special project for Linux
3 ;; Copyright (C) 2008-2014 Free Software Foundation, Inc.
5 ;; Author: Eric M. Ludlam <eric@siege-engine.com>
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 ;; Provide a special project type just for Linux, cause Linux is special.
26 ;; Identifies a Linux project automatically.
27 ;; Speedy ede-expand-filename based on extension.
28 ;; Pre-populates the preprocessor map from lisp.h
30 ;; ToDo :
31 ;; * Add "build" options.
32 ;; * Add texinfo lookup options.
33 ;; * Add website
35 (eval-when-compile (require 'cl))
37 (require 'ede)
38 (require 'ede/make)
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")
44 ;;; Code:
45 (defgroup project-linux nil
46 "File and tag browser frame."
47 :group 'tools
48 :group 'ede
49 :version "24.3")
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)))
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)))
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)
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)
76 (defvar ede-linux-project-list nil
77 "List of projects created by option `ede-linux-project'.")
79 (defun ede-linux-file-existing (dir)
80 "Find a Linux project in the list of Linux projects.
81 DIR is the directory to search from."
82 (let ((projs ede-linux-project-list)
83 (ans nil))
84 (while (and projs (not ans))
85 (let ((root (ede-project-root-directory (car projs))))
86 (when (string-match (concat "^" (regexp-quote root)) dir)
87 (setq ans (car projs))))
88 (setq projs (cdr projs)))
89 ans))
91 ;;;###autoload
92 (defun ede-linux-project-root (&optional dir)
93 "Get the root directory for DIR."
94 (when (not dir) (setq dir default-directory))
95 (let ((case-fold-search t)
96 (proj (ede-linux-file-existing dir)))
97 (if proj
98 (ede-up-directory (file-name-directory
99 (oref proj :file)))
100 ;; No pre-existing project. Let's take a wild-guess if we have
101 ;; an Linux project here.
102 (when (string-match "linux[^/]*" dir)
103 (let ((base (substring dir 0 (match-end 0))))
104 (when (file-exists-p (expand-file-name "scripts/ver_linux" base))
105 base))))))
107 (defun ede-linux-version (dir)
108 "Find the Linux version for the Linux src in DIR."
109 (let ((buff (get-buffer-create " *linux-query*")))
110 (with-current-buffer buff
111 (erase-buffer)
112 (setq default-directory (file-name-as-directory dir))
113 (insert-file-contents "Makefile" nil 0 512)
114 (goto-char (point-min))
115 (let (major minor sub)
116 (re-search-forward "^VERSION *= *\\([0-9.]+\\)")
117 (setq major (match-string 1))
118 (re-search-forward "^PATCHLEVEL *= *\\([0-9.]+\\)")
119 (setq minor (match-string 1))
120 (re-search-forward "^SUBLEVEL *= *\\([0-9.]+\\)")
121 (setq sub (match-string 1))
122 (prog1
123 (concat major "." minor "." sub)
124 (kill-buffer buff)
125 )))))
127 (defclass ede-linux-project (ede-project eieio-instance-tracker)
128 ((tracking-symbol :initform 'ede-linux-project-list)
129 (build-directory :initarg :build-directory
130 :type string
131 :documentation "Build directory.")
132 (architecture :initarg :architecture
133 :type string
134 :documentation "Target architecture.")
135 (include-path :initarg :include-path
136 :type list
137 :documentation "Include directories.
138 Contains both common and target architecture-specific directories."))
139 "Project Type for the Linux source code."
140 :method-invocation-order :depth-first)
143 (defun ede-linux--get-build-directory (dir)
144 "Detect build directory for sources in DIR.
145 If DIR has not been used as a build directory, fall back to
146 `project-linux-build-directory-default'."
148 ;; detected build on source directory
149 (and (file-exists-p (expand-file-name ".config" dir)) dir)
150 ;; use configuration
151 (case project-linux-build-directory-default
152 (same dir)
153 (ask (read-directory-name "Select Linux' build directory: " dir)))))
156 (defun ede-linux--get-archs (dir)
157 "Returns a list of architecture names found in DIR."
158 (let ((archs-dir (expand-file-name "arch" dir))
159 archs)
160 (when (file-directory-p archs-dir)
161 (mapc (lambda (elem)
162 (when (and
163 (not (string= elem "."))
164 (not (string= elem ".."))
165 (not (string= elem "x86_64")) ; has no separate sources
166 (file-directory-p
167 (expand-file-name elem archs-dir)))
168 (add-to-list 'archs elem t)))
169 (directory-files archs-dir)))
170 archs))
173 (defun ede-linux--detect-architecture (dir)
174 "Try to auto-detect the architecture as configured in DIR.
175 DIR is Linux' build directory. If it cannot be auto-detected,
176 returns `project-linux-architecture-default'."
177 (let ((archs-dir (expand-file-name "arch" dir))
178 (archs (ede-linux--get-archs dir))
179 arch found)
180 (or (and
181 archs
182 ;; Look for /arch/<arch>/include/generated
183 (progn
184 (while (and archs (not found))
185 (setq arch (car archs))
186 (when (file-directory-p
187 (expand-file-name (concat arch "/include/generated")
188 archs-dir))
189 (setq found arch))
190 (setq archs (cdr archs)))
191 found))
192 project-linux-architecture-default)))
194 (defun ede-linux--get-architecture (dir bdir)
195 "Try to auto-detect the architecture as configured in BDIR.
196 Uses `ede-linux--detect-architecture' for the auto-detection. If
197 the result is `ask', let the user choose from architectures found
198 in DIR."
199 (let ((arch (ede-linux--detect-architecture bdir)))
200 (case arch
201 (ask
202 (completing-read "Select target architecture: "
203 (ede-linux--get-archs dir)))
204 (t arch))))
207 (defun ede-linux--include-path (dir bdir arch)
208 "Returns a list with include directories.
209 Returned directories might not exist, since they are not created
210 until Linux is built for the first time."
211 (map 'list
212 (lambda (elem) (format (concat (car elem) "/" (cdr elem)) arch))
213 ;; XXX: taken from the output of "make V=1"
214 (list (cons dir "arch/%s/include")
215 (cons bdir "arch/%s/include/generated")
216 (cons dir "include")
217 (cons bdir "include")
218 (cons dir "arch/%s/include/uapi")
219 (cons bdir "arch/%s/include/generated/uapi")
220 (cons dir "include/uapi")
221 (cons bdir "include/generated/uapi"))))
223 ;;;###autoload
224 (defun ede-linux-load (dir &optional rootproj)
225 "Return an Linux Project object if there is a match.
226 Return nil if there isn't one.
227 Argument DIR is the directory it is created for.
228 ROOTPROJ is nil, since there is only one project."
229 (or (ede-linux-file-existing dir)
230 ;; Doesn't already exist, so let's make one.
231 (let* ((bdir (ede-linux--get-build-directory dir))
232 (arch (ede-linux--get-architecture dir bdir))
233 (include-path (ede-linux--include-path dir bdir arch))
234 (proj (ede-linux-project
235 "Linux"
236 :name "Linux"
237 :version (ede-linux-version dir)
238 :directory (file-name-as-directory dir)
239 :file (expand-file-name "scripts/ver_linux"
240 dir)
241 :build-directory bdir
242 :architecture arch
243 :include-path include-path)))
244 (ede-add-project-to-global-list proj))))
246 ;;;###autoload
247 (ede-add-project-autoload
248 (ede-project-autoload "linux"
249 :name "LINUX ROOT"
250 :file 'ede/linux
251 :proj-file "scripts/ver_linux"
252 :proj-root-dirmatch "linux[^/]*"
253 :proj-root 'ede-linux-project-root
254 :load-type 'ede-linux-load
255 :class-sym 'ede-linux-project
256 :new-p nil
257 :safe-p t)
258 'unique)
260 (defclass ede-linux-target-c (ede-target)
262 "EDE Linux Project target for C code.
263 All directories need at least one target.")
265 (defclass ede-linux-target-misc (ede-target)
267 "EDE Linux Project target for Misc files.
268 All directories need at least one target.")
270 (defmethod initialize-instance ((this ede-linux-project)
271 &rest fields)
272 "Make sure the targets slot is bound."
273 (call-next-method)
274 (unless (slot-boundp this 'targets)
275 (oset this :targets nil)))
277 ;;; File Stuff
279 (defmethod ede-project-root-directory ((this ede-linux-project)
280 &optional file)
281 "Return the root for THIS Linux project with file."
282 (ede-up-directory (file-name-directory (oref this file))))
284 (defmethod ede-project-root ((this ede-linux-project))
285 "Return my root."
286 this)
288 (defmethod ede-find-subproject-for-directory ((proj ede-linux-project)
289 dir)
290 "Return PROJ, for handling all subdirs below DIR."
291 proj)
293 ;;; TARGET MANAGEMENT
295 (defun ede-linux-find-matching-target (class dir targets)
296 "Find a target that is a CLASS and is in DIR in the list of TARGETS."
297 (let ((match nil))
298 (dolist (T targets)
299 (when (and (object-of-class-p T class)
300 (string= (oref T :path) dir))
301 (setq match T)
303 match))
305 (defmethod ede-find-target ((proj ede-linux-project) buffer)
306 "Find an EDE target in PROJ for BUFFER.
307 If one doesn't exist, create a new one for this directory."
308 (let* ((ext (file-name-extension (buffer-file-name buffer)))
309 (cls (cond ((not ext)
310 'ede-linux-target-misc)
311 ((string-match "c\\|h" ext)
312 'ede-linux-target-c)
313 (t 'ede-linux-target-misc)))
314 (targets (oref proj targets))
315 (dir default-directory)
316 (ans (ede-linux-find-matching-target cls dir targets))
318 (when (not ans)
319 (setq ans (make-instance
321 :name (file-name-nondirectory
322 (directory-file-name dir))
323 :path dir
324 :source nil))
325 (object-add-to-list proj :targets ans)
327 ans))
329 ;;; UTILITIES SUPPORT.
331 (defmethod ede-preprocessor-map ((this ede-linux-target-c))
332 "Get the pre-processor map for Linux C code.
333 All files need the macros from lisp.h!"
334 (require 'semantic/db)
335 (let* ((proj (ede-target-parent this))
336 (root (ede-project-root proj))
337 (versionfile (ede-expand-filename root "include/linux/version.h"))
338 (table (when (and versionfile (file-exists-p versionfile))
339 (semanticdb-file-table-object versionfile)))
340 (filemap '( ("__KERNEL__" . "")
343 (when table
344 (when (semanticdb-needs-refresh-p table)
345 (semanticdb-refresh-table table))
346 (setq filemap (append filemap (oref table lexical-table)))
348 filemap
351 (defun ede-linux-file-exists-name (name root subdir)
352 "Return a file name if NAME exists under ROOT with SUBDIR in between."
353 (let ((F (expand-file-name name (expand-file-name subdir root))))
354 (when (file-exists-p F) F)))
356 (defmethod ede-expand-filename-impl ((proj ede-linux-project) name)
357 "Within this project PROJ, find the file NAME.
358 Knows about how the Linux source tree is organized."
359 (let* ((ext (file-name-extension name))
360 (root (ede-project-root proj))
361 (dir (ede-project-root-directory root))
362 (bdir (oref proj build-directory))
363 (F (cond
364 ((not ext) nil)
365 ((string-match "h" ext)
366 (let ((dirs (oref proj include-path))
367 found)
368 (while (and dirs (not found))
369 (setq found
370 (or (ede-linux-file-exists-name name bdir (car dirs))
371 (ede-linux-file-exists-name name dir (car dirs))))
372 (setq dirs (cdr dirs)))
373 found))
374 ((string-match "txt" ext)
375 (ede-linux-file-exists-name name dir "Documentation"))
376 (t nil))))
377 (or F (call-next-method))))
379 (defmethod project-compile-project ((proj ede-linux-project)
380 &optional command)
381 "Compile the entire current project.
382 Argument COMMAND is the command to use when compiling."
383 (let* ((dir (ede-project-root-directory proj)))
385 (require 'compile)
386 (if (not project-linux-compile-project-command)
387 (setq project-linux-compile-project-command compile-command))
388 (if (not command)
389 (setq command
390 (format
391 project-linux-compile-project-command
392 dir)))
394 (compile command)))
396 (defmethod project-compile-target ((obj ede-linux-target-c) &optional command)
397 "Compile the current target.
398 Argument COMMAND is the command to use for compiling the target."
399 (let* ((proj (ede-target-parent obj))
400 (root (ede-project-root proj))
401 (dir (ede-project-root-directory root))
402 (subdir (oref obj path)))
404 (require 'compile)
405 (if (not project-linux-compile-project-command)
406 (setq project-linux-compile-project-command compile-command))
407 (if (not command)
408 (setq command
409 (format
410 project-linux-compile-target-command
411 dir subdir)))
413 (compile command)))
415 (provide 'ede/linux)
417 ;; Local variables:
418 ;; generated-autoload-file: "loaddefs.el"
419 ;; generated-autoload-load-name: "ede/linux"
420 ;; End:
422 ;;; ede/linux.el ends here