Merge from origin/emacs-25
[emacs.git] / lisp / cedet / ede / linux.el
blob22f5c3ed218a44d3fb7ac1726be39b7c58f4abb3
1 ;;; ede/linux.el --- Special project for Linux
3 ;; Copyright (C) 2008-2017 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 (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 )))))
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)
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'."
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)))))
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))
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)))
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))))
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"))))
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)))
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)
223 (defclass ede-linux-target-c (ede-target)
225 "EDE Linux Project target for C code.
226 All directories need at least one target.")
228 (defclass ede-linux-target-misc (ede-target)
230 "EDE Linux Project target for Misc files.
231 All directories need at least one target.")
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)))
240 ;;; File Stuff
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))))
247 (cl-defmethod ede-project-root ((this ede-linux-project))
248 "Return my root."
249 this)
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)
256 ;;; TARGET MANAGEMENT
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)
266 match))
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))
281 (when (not ans)
282 (setq ans (make-instance
284 :name (file-name-nondirectory
285 (directory-file-name dir))
286 :path dir
287 :source nil))
288 (object-add-to-list proj :targets ans)
290 ans))
292 ;;; UTILITIES SUPPORT.
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__" . "")
306 (when table
307 (when (semanticdb-needs-refresh-p table)
308 (semanticdb-refresh-table table))
309 (setq filemap (append filemap (oref table lexical-table)))
311 filemap
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)))
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))))
342 ;;; Command Support
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)))
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)))
359 (compile command)))
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)))
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)))
378 (compile command)))
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)
393 (provide 'ede/linux)
395 ;; Local variables:
396 ;; generated-autoload-file: "loaddefs.el"
397 ;; generated-autoload-load-name: "ede/linux"
398 ;; End:
400 ;;; ede/linux.el ends here