[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: New Flymake rewrite in emacs-26

From: João Távora
Subject: Re: New Flymake rewrite in emacs-26
Date: Wed, 11 Oct 2017 14:41:46 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/26.0.60 (gnu/linux)

address@hidden (João Távora) writes:

> No, I was aiming for something more generic that includes at least Emacs
> and perhaps other GNU projects.

FWIW, here's what I hacked up so far. Seems to work OK in two GNU
projects: Emacs and Hello (after ./configure, of course).

Probably flawed, but it's a start. Feedback welcome. To test, just load
this file and M-x flymake-mode in a C file.

Another option, as suggested previously, is to have a special Makefile
target (that'll need reconfiguring for each project, though).


;;; flymake-gcc.el --- naive gcc Flymake backend -*- lexical-binding: t; -*-

(defvar flymake-gcc-program "gcc"
  "GCC program")

(defun flymake--gcc-heroic-unescape (string)
    (let ((error-buffer (current-buffer)))
           ;; I suspect "shell-command" makes windows even when called
           ;; from lisp.
               "%s -Q --batch --eval \"%s\" -- %s"
               (expand-file-name invocation-name
               "(mapc 'print (nthcdr 4 command-line-args))"
          (goto-char (point-min))
          (cl-loop with eof = (make-symbol "eof")
                   for form =
                   (condition-case _err
                       (read (current-buffer))
                     (error eof))
                   while (not (eq form eof))
                   collect form))
          (with-current-buffer error-buffer
            (error (buffer-string)))))))))

(defvar flymake-gcc-flags 'flymake-gcc-guess-flags
  "A list of flags passed to GCC.
Alternatively, a symbol naming a function called with no
arguments that should produce this list of flags, or error if it
cannot do so.")

(defvar flymake-gcc-extra-flags '("-Wextra" "-Wall")
  "A list of extra flags passed to GCC.")

(defvar-local flymake--gcc-cached-flags nil
  "Internal variable for `flymake-gcc-guess-flags'")

(defun flymake-gcc-guess-flags (&optional trash-cache)
  "Guess GCC flags for compiling current buffer "
  (interactive "P")
  (unless (executable-find "make") (error "Cannot find a suitable make"))
  (when trash-cache (setq flymake--gcc-cached-flags nil))
  (catch 'retval
    (unless (buffer-file-name)
      ;; don't error and don't cache, so that when the buffer is saved
      ;; we get another chance.
      (throw 'retval nil))
    (when-let* ((makefile-dir
                 (locate-dominating-file default-directory "Makefile"))
                (makefile (expand-file-name "Makefile" makefile-dir))
                (mtime (file-attribute-modification-time
                        (file-attributes makefile))))
       ((equal (list makefile mtime)
               (cdr flymake--gcc-cached-flags))
        (when (called-interactively-p 'interactive)
          (message "cached hit for flags for this buffer: %s"
                   (car flymake--gcc-cached-flags)))
        (throw 'retval (car flymake--gcc-cached-flags)))
             (blob (shell-command-to-string
                    (format "make -C %s -f %s --just-print %s.o"
             (match (string-match
                     (format "gcc[[:space:]]+\\(\\(?:-.*\\)*\\)%s"
             (flag-string (and match
                               (match-string 1 blob)))
             (flags (and flag-string
                         (flymake--gcc-heroic-unescape flag-string))))
          (when (or flags (string= "" flag-string))
            (setq flymake--gcc-cached-flags (list flags makefile mtime))
            (when (called-interactively-p 'interactive)
              (message "cached miss for flags for this buffer: %s" flags))
            (throw 'retval flags))))))
    (error "Could not guess gcc flags")))

(defvar-local flymake--gcc-proc nil
  "Internal variable for `flymake-gcc'")

(defun flymake-gcc (report-fn &rest _args)
  "Flymake backend for GCC"
  (unless (executable-find flymake-gcc-program)
    (error "Cannot find a suitable gcc"))
  (when (process-live-p flymake--gcc-proc)
    (kill-process flymake--gcc-proc))
  (let ((source (current-buffer)))
      (setq flymake--gcc-proc
             :name "gcc-flymake"
             :buffer (generate-new-buffer "*gcc-flymake*")
             :command `(,flymake-gcc-program
                        ,@(if (symbolp flymake-gcc-flags)
                              (funcall flymake-gcc-flags)
                        "-x" "c" "-")
             :noquery t :connection-type 'pipe
             (lambda (p _ev)
               (when (eq 'exit (process-status p))
                     (when (eq p flymake--gcc-proc)
                       (with-current-buffer (process-buffer p)
                         (goto-char (point-min))
                          while (search-forward-regexp
                                 "^<stdin>:\\([0-9]+\\):\\([0-9]+\\): \\(.*\\): 
                                 nil t)
                          for msg = (match-string 4)
                          for (beg . end) =
                           (string-to-number (match-string 1))
                           (string-to-number (match-string 2)))
                          for type = (assoc-default
                                      (match-string 3)
                                      '(("error" . :error)
                                        ("note" . :note)
                                        ("warning" . :warning))
                          collect (flymake-make-diagnostic source beg end type 
                          into diags
                          finally (funcall report-fn diags))))
                   ;; (display-buffer (process-buffer p)) ; use this instead of 
the next one for debug
                   (kill-buffer (process-buffer p))
      (process-send-region flymake--gcc-proc (point-min) (point-max))
      (process-send-eof flymake--gcc-proc))))

(defun flymake--setup-gcc-flymake ()
  (add-hook 'flymake-diagnostic-functions 'flymake-gcc nil t))

(add-hook 'c-mode-hook 'flymake--setup-gcc-flymake)

;;; flymake-gcc.el ends here

reply via email to

[Prev in Thread] Current Thread [Next in Thread]