compile.el 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. ;; Run compiler as inferior of Emacs, and parse its error messages.
  2. ;; Copyright (C) 1985, 1986 Free Software Foundation, Inc.
  3. ;; This file is part of GNU Emacs.
  4. ;; GNU Emacs is distributed in the hope that it will be useful,
  5. ;; but WITHOUT ANY WARRANTY. No author or distributor
  6. ;; accepts responsibility to anyone for the consequences of using it
  7. ;; or for whether it serves any particular purpose or works at all,
  8. ;; unless he says so in writing. Refer to the GNU Emacs General Public
  9. ;; License for full details.
  10. ;; Everyone is granted permission to copy, modify and redistribute
  11. ;; GNU Emacs, but only under the conditions described in the
  12. ;; GNU Emacs General Public License. A copy of this license is
  13. ;; supposed to have been given to you along with GNU Emacs so you
  14. ;; can know your rights and responsibilities. It should be in a
  15. ;; file named COPYING. Among other things, the copyright notice
  16. ;; and this notice must be preserved on all copies.
  17. (provide 'compile)
  18. (defvar compilation-process nil
  19. "Process created by compile command, or nil if none exists now.
  20. Note that the process may have been \"deleted\" and still
  21. be the value of this variable.")
  22. (defvar compilation-error-list nil
  23. "List of error message descriptors for visiting erring functions.
  24. Each error descriptor is a list of length two.
  25. Its car is a marker pointing to an error message.
  26. Its cadr is a marker pointing to the text of the line the message is about,
  27. or nil if that is not interesting.
  28. The value may be t instead of a list;
  29. this means that the buffer of error messages should be reparsed
  30. the next time the list of errors is wanted.")
  31. (defvar compilation-parsing-end nil
  32. "Position of end of buffer when last error messages parsed.")
  33. (defvar compilation-error-message nil
  34. "Message to print when no more matches for compilation-error-regexp are found")
  35. (defvar compilation-error-regexp
  36. "\\([^ \n]+\\(: *\\|, line \\|(\\)[0-9]+\\)\\|\\([0-9]+.*of *[^ \n]+\\)"
  37. "Regular expression for filename/linenumber in error in compilation log.")
  38. (defun compile (command)
  39. "Compile the program including the current buffer. Default: run make.
  40. Runs COMMAND, a shell command, in a separate process asynchronously
  41. with output going to the buffer *compilation*.
  42. You can then use the command \\[next-error] to find the next error message
  43. and move to the source code that caused it."
  44. (interactive (list (read-string "Compile command: " compile-command)))
  45. (setq compile-command command)
  46. (compile1 compile-command "No more errors"))
  47. (defun grep (command)
  48. "Run grep, with user-specified args, and collect output in a buffer.
  49. While grep runs asynchronously, you can use the \\[next-error] command
  50. to find the text that grep hits refer to."
  51. (interactive "sRun grep (with args): ")
  52. (compile1 (concat "grep -n " command " /dev/null")
  53. "No more grep hits" "grep"))
  54. (defun compile1 (command error-message &optional name-of-mode)
  55. (save-some-buffers)
  56. (if compilation-process
  57. (if (or (not (eq (process-status compilation-process) 'run))
  58. (yes-or-no-p "A compilation process is running; kill it? "))
  59. (condition-case ()
  60. (progn
  61. (interrupt-process compilation-process)
  62. (sit-for 1)
  63. (delete-process compilation-process))
  64. (error nil))
  65. (error "Cannot have two compilation processes")))
  66. (setq compilation-process nil)
  67. (compilation-forget-errors)
  68. (setq compilation-error-list t)
  69. (setq compilation-error-message error-message)
  70. (setq compilation-process
  71. (start-process "compilation" "*compilation*"
  72. shell-file-name
  73. "-c" (concat "exec " command)))
  74. (with-output-to-temp-buffer "*compilation*"
  75. (princ "cd ")
  76. (princ default-directory)
  77. (terpri)
  78. (princ command)
  79. (terpri))
  80. (let ((regexp compilation-error-regexp))
  81. (save-excursion
  82. (switch-to-buffer "*compilation*")
  83. (make-local-variable 'compilation-error-regexp)
  84. (setq compilation-error-regexp regexp)))
  85. (set-process-sentinel compilation-process 'compilation-sentinel)
  86. (let* ((thisdir default-directory)
  87. (outbuf (process-buffer compilation-process))
  88. (outwin (get-buffer-window outbuf)))
  89. (if (eq outbuf (current-buffer))
  90. (goto-char (point-max)))
  91. (save-excursion
  92. (set-buffer outbuf)
  93. (buffer-flush-undo outbuf)
  94. (let ((start (save-excursion (set-buffer outbuf) (point-min))))
  95. (set-window-start outwin start)
  96. (or (eq outwin (selected-window))
  97. (set-window-point outwin start)))
  98. (setq default-directory thisdir)
  99. (fundamental-mode)
  100. (setq mode-name (or name-of-mode "Compilation"))
  101. ;; Make log buffer's mode line show process state
  102. (setq mode-line-process '(": %s")))))
  103. ;; Called when compilation process changes state.
  104. (defun compilation-sentinel (proc msg)
  105. (cond ((null (buffer-name (process-buffer proc)))
  106. ;; buffer killed
  107. (set-process-buffer proc nil))
  108. ((memq (process-status proc) '(signal exit))
  109. (let* ((obuf (current-buffer))
  110. (omax (point-max))
  111. (opoint (point)))
  112. ;; save-excursion isn't the right thing if
  113. ;; process-buffer is current-buffer
  114. (unwind-protect
  115. (progn
  116. ;; Write something in *compilation* and hack its mode line,
  117. (set-buffer (process-buffer proc))
  118. (goto-char (point-max))
  119. (insert ?\n mode-name " " msg)
  120. (forward-char -1)
  121. (insert " at "
  122. (substring (current-time-string) 0 -5))
  123. (forward-char 1)
  124. (setq mode-line-process
  125. (concat ": "
  126. (symbol-name (process-status proc))))
  127. ;; If buffer and mode line will show that the process
  128. ;; is dead, we can delete it now. Otherwise it
  129. ;; will stay around until M-x list-processes.
  130. (delete-process proc))
  131. (setq compilation-process nil)
  132. ;; Force mode line redisplay soon
  133. (set-buffer-modified-p (buffer-modified-p)))
  134. (if (< opoint omax)
  135. (goto-char opoint))
  136. (set-buffer obuf)))))
  137. (defun kill-compilation ()
  138. "Kill the process made by the \\[compile] command."
  139. (interactive)
  140. (if compilation-process
  141. (interrupt-process compilation-process)))
  142. (defun kill-grep ()
  143. "Kill the process made by the \\[grep] command."
  144. (interactive)
  145. (if compilation-process
  146. (interrupt-process compilation-process)))
  147. (defun next-error (&optional argp)
  148. "Visit next compilation error message and corresponding source code.
  149. This operates on the output from the \\[compile] command.
  150. If all preparsed error messages have been processed,
  151. the error message buffer is checked for new ones.
  152. A non-nil argument (prefix arg, if interactive)
  153. means reparse the error message buffer and start at the first error."
  154. (interactive "P")
  155. (if (or (eq compilation-error-list t)
  156. argp)
  157. (progn (compilation-forget-errors)
  158. (setq compilation-parsing-end 1)))
  159. (if compilation-error-list
  160. nil
  161. (save-excursion
  162. (switch-to-buffer "*compilation*")
  163. (set-buffer-modified-p nil)
  164. (compilation-parse-errors)))
  165. (let ((next-error (car compilation-error-list)))
  166. (if (null next-error)
  167. (error (concat compilation-error-message
  168. (if (and compilation-process
  169. (eq (process-status compilation-process)
  170. 'run))
  171. " yet" ""))))
  172. (setq compilation-error-list (cdr compilation-error-list))
  173. (if (null (car (cdr next-error)))
  174. nil
  175. (switch-to-buffer (marker-buffer (car (cdr next-error))))
  176. (goto-char (car (cdr next-error)))
  177. (set-marker (car (cdr next-error)) nil))
  178. (let* ((pop-up-windows t)
  179. (w (display-buffer (marker-buffer (car next-error)))))
  180. (set-window-point w (car next-error))
  181. (set-window-start w (car next-error)))
  182. (set-marker (car next-error) nil)))
  183. ;; Set compilation-error-list to nil, and
  184. ;; unchain the markers that point to the error messages and their text,
  185. ;; so that they no longer slow down gap motion.
  186. ;; This would happen anyway at the next garbage collection,
  187. ;; but it is better to do it right away.
  188. (defun compilation-forget-errors ()
  189. (if (eq compilation-error-list t)
  190. (setq compilation-error-list nil))
  191. (while compilation-error-list
  192. (let ((next-error (car compilation-error-list)))
  193. (set-marker (car next-error) nil)
  194. (if (car (cdr next-error))
  195. (set-marker (car (cdr next-error)) nil)))
  196. (setq compilation-error-list (cdr compilation-error-list))))
  197. (defun compilation-parse-errors ()
  198. "Parse the current buffer as error messages.
  199. This makes a list of error descriptors, compilation-error-list.
  200. For each source-file, line-number pair in the buffer,
  201. the source file is read in, and the text location is saved in compilation-error-list.
  202. The function next-error, assigned to \\[next-error], takes the next error off the list
  203. and visits its location."
  204. (setq compilation-error-list nil)
  205. (message "Parsing error messages...")
  206. (let (text-buffer
  207. last-filename last-linenum)
  208. ;; Don't reparse messages already seen at last parse.
  209. (goto-char compilation-parsing-end)
  210. ;; Don't parse the first two lines as error messages.
  211. ;; This matters for grep.
  212. (if (bobp)
  213. (forward-line 2))
  214. (while (re-search-forward compilation-error-regexp nil t)
  215. (let (linenum filename
  216. error-marker text-marker)
  217. ;; Extract file name and line number from error message.
  218. (save-restriction
  219. (narrow-to-region (match-beginning 0) (match-end 0))
  220. (goto-char (point-max))
  221. (skip-chars-backward "[0-9]")
  222. ;; If it's a lint message, use the last file(linenum) on the line.
  223. ;; Normally we use the first on the line.
  224. (if (= (preceding-char) ?\()
  225. (progn
  226. (narrow-to-region (point-min) (1+ (buffer-size)))
  227. (end-of-line)
  228. (re-search-backward compilation-error-regexp)
  229. (skip-chars-backward "^ \t\n")
  230. (narrow-to-region (point) (match-end 0))
  231. (goto-char (point-max))
  232. (skip-chars-backward "[0-9]")))
  233. ;; Are we looking at a "filename-first" or "line-number-first" form?
  234. (if (looking-at "[0-9]")
  235. (progn
  236. (setq linenum (read (current-buffer)))
  237. (goto-char (point-min)))
  238. ;; Line number at start, file name at end.
  239. (progn
  240. (goto-char (point-min))
  241. (setq linenum (read (current-buffer)))
  242. (goto-char (point-max))
  243. (skip-chars-backward "^ \t\n")))
  244. (setq filename (compilation-grab-filename)))
  245. ;; Locate the erring file and line.
  246. (if (and (equal filename last-filename)
  247. (= linenum last-linenum))
  248. nil
  249. (beginning-of-line 1)
  250. (setq error-marker (point-marker))
  251. ;; text-buffer gets the buffer containing this error's file.
  252. (if (not (equal filename last-filename))
  253. (setq text-buffer
  254. (and (file-exists-p (setq last-filename filename))
  255. (find-file-noselect filename))
  256. last-linenum 0))
  257. (if text-buffer
  258. ;; Go to that buffer and find the erring line.
  259. (save-excursion
  260. (set-buffer text-buffer)
  261. (if (zerop last-linenum)
  262. (progn
  263. (goto-char 1)
  264. (setq last-linenum 1)))
  265. (forward-line (- linenum last-linenum))
  266. (setq last-linenum linenum)
  267. (setq text-marker (point-marker))
  268. (setq compilation-error-list
  269. (cons (list error-marker text-marker)
  270. compilation-error-list)))))
  271. (forward-line 1)))
  272. (setq compilation-parsing-end (point-max)))
  273. (message "Parsing error messages...done")
  274. (setq compilation-error-list (nreverse compilation-error-list)))
  275. (defun compilation-grab-filename ()
  276. "Return a string which is a filename, starting at point.
  277. Ignore quotes and parentheses around it, as well as trailing colons."
  278. (if (eq (following-char) ?\")
  279. (save-restriction
  280. (narrow-to-region (point)
  281. (progn (forward-sexp 1) (point)))
  282. (goto-char (point-min))
  283. (read (current-buffer)))
  284. (buffer-substring (point)
  285. (progn
  286. (skip-chars-forward "^ :,\n\t(")
  287. (point)))))
  288. (define-key ctl-x-map "`" 'next-error)