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/>.
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
31 ;; * Add "build" options.
32 ;; * Add texinfo lookup options.
35 (eval-when-compile (require 'cl
))
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")
45 (defgroup project-linux nil
46 "File and tag browser frame."
51 (defcustom project-linux-build-directory-default
'ask
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."
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."
71 (defcustom project-linux-compile-project-command
(concat ede-make-command
" -k -C %s")
72 "Default command used to compile a project."
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
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))
92 (concat major
"." minor
"." sub
)
96 (defclass ede-linux-project
(ede-project)
97 ((build-directory :initarg
:build-directory
99 :documentation
"Build directory.")
100 (architecture :initarg
:architecture
102 :documentation
"Target architecture.")
103 (include-path :initarg
:include-path
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
)
119 (case project-linux-build-directory-default
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
))
128 (when (file-directory-p archs-dir
)
131 (not (string= elem
"."))
132 (not (string= elem
".."))
133 (not (string= elem
"x86_64")) ; has no separate sources
135 (expand-file-name elem archs-dir
)))
136 (add-to-list 'archs elem t
)))
137 (directory-files archs-dir
)))
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
))
150 ;; Look for /arch/<arch>/include/generated
152 (while (and archs
(not found
))
153 (setq arch
(car archs
))
154 (when (file-directory-p
155 (expand-file-name (concat arch
"/include/generated")
158 (setq archs
(cdr archs
)))
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
167 (let ((arch (ede-linux--detect-architecture bdir
)))
170 (completing-read "Select target architecture: "
171 (ede-linux--get-archs dir
)))
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."
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")
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"))))
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
203 :version
(ede-linux-version dir
)
204 :directory
(file-name-as-directory dir
)
205 :file
(expand-file-name "scripts/ver_linux"
207 :build-directory bdir
209 :include-path include-path
)))
212 (ede-add-project-autoload
213 (make-instance 'ede-project-autoload
216 :proj-file
"scripts/ver_linux"
217 :load-type
'ede-linux-load
218 :class-sym
'ede-linux-project
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
)
235 "Make sure the targets slot is bound."
236 (cl-call-next-method)
237 (unless (slot-boundp this
'targets
)
238 (oset this
:targets nil
)))
242 (cl-defmethod ede-project-root-directory ((this ede-linux-project
)
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
))
251 (cl-defmethod ede-find-subproject-for-directory ((proj ede-linux-project
)
253 "Return PROJ, for handling all subdirs below DIR."
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."
262 (when (and (object-of-class-p T class
)
263 (string= (oref T path
) dir
))
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
)
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
))
282 (setq ans
(make-instance
284 :name
(file-name-nondirectory
285 (directory-file-name dir
))
288 (object-add-to-list proj
:targets 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__" .
"")
307 (when (semanticdb-needs-refresh-p table
)
308 (semanticdb-refresh-table table
))
309 (setq filemap
(append filemap
(oref table lexical-table
)))
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
))
328 ((string-match "h" ext
)
329 (let ((dirs (oref proj include-path
))
331 (while (and dirs
(not 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
)))
337 ((string-match "txt" ext
)
338 (ede-linux-file-exists-name name dir
"Documentation"))
340 (or F
(cl-call-next-method))))
344 (cl-defmethod project-compile-project ((proj ede-linux-project
)
346 "Compile the entire current project.
347 Argument COMMAND is the command to use when compiling."
348 (let* ((dir (ede-project-root-directory proj
)))
351 (if (not project-linux-compile-project-command
)
352 (setq project-linux-compile-project-command compile-command
))
356 project-linux-compile-project-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
)))
370 (if (not project-linux-compile-project-command
)
371 (setq project-linux-compile-project-command compile-command
))
375 project-linux-compile-target-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
)
396 ;; generated-autoload-file: "loaddefs.el"
397 ;; generated-autoload-load-name: "ede/linux"
400 ;;; ede/linux.el ends here