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