From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: joaotavora@gmail.com (=?utf-8?B?Sm/Do28gVMOhdm9yYQ==?=) Newsgroups: gmane.emacs.devel Subject: [PATCH] Flymake support for C/C++ Date: Thu, 12 Oct 2017 16:09:15 +0100 Message-ID: <87zi8wmmhw.fsf@gmail.com> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1507820988 3219 195.159.176.226 (12 Oct 2017 15:09:48 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Thu, 12 Oct 2017 15:09:48 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.0.90 (gnu/linux) Cc: acm@muc.de, eliz@gnu.org, npostavs@users.sourceforge.net, sdl.web@gmail.com, monnier@iro.umontreal.ca To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Thu Oct 12 17:09:42 2017 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1e2f79-0007t4-J9 for ged-emacs-devel@m.gmane.org; Thu, 12 Oct 2017 17:09:35 +0200 Original-Received: from localhost ([::1]:46015 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1e2f7G-0005cW-V4 for ged-emacs-devel@m.gmane.org; Thu, 12 Oct 2017 11:09:43 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:55778) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1e2f73-0005bL-Ga for emacs-devel@gnu.org; Thu, 12 Oct 2017 11:09:33 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1e2f6y-000342-Av for emacs-devel@gnu.org; Thu, 12 Oct 2017 11:09:29 -0400 Original-Received: from mail-wm0-x22e.google.com ([2a00:1450:400c:c09::22e]:44561) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1e2f6t-0002yR-9X; Thu, 12 Oct 2017 11:09:19 -0400 Original-Received: by mail-wm0-x22e.google.com with SMTP id 196so24334483wma.1; Thu, 12 Oct 2017 08:09:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:user-agent:mime-version; bh=uwXYy5tGx3yMdcb0Z4/ZtpBF9ejI6WJTMeGKUt0kHn8=; b=d457/j8GvoOgb1g/6DmFknorRfKoiv9Mkr5r93ntUYkNNmqE0+tS00n6/MEbqxdyOy uCs07PTQWI7QACaB1Qg0F2I8eJ6RcutoV39dlk+s3PLqKjLiVgL7tidfWO1DmD1ecb+y 4qQXQivXy5dissgsj+T5YPc4JbTnfvdKPeAH2Z2EV4Rd+UK/MI2spBRxtxgkg5F1R+xF G3DwC/N4PqNPzrnMcruIEo5GOmcbExojzGqzfssxxZsOnN5BvQmnXbQG2UIv/yS7j7Nf HkFWMcaC4u8OVD9EcnkvAQJs0eMRNXfytKiTwd3i/xLzo3hO8Il19dPGFZpdJ/vsD88r Aq0Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:user-agent :mime-version; bh=uwXYy5tGx3yMdcb0Z4/ZtpBF9ejI6WJTMeGKUt0kHn8=; b=JwHvQO1SsaBNUm6ZzgAdroz8H+jd1eP6JDap2Uv1/U+NTZ4OS3uu2sYpHDWnvv+MKX 1X0XTvpis0asoUn3lze2nnD8WP8WdN1T+c+Ebf0oRcCDx1HGtQuiv1nmAVcBK0TMDwb3 v1G0xtjFVvdVffLSuPviVfdN1z6ZRdrUPTrI+SItO8p2mKMiCoqJoMOVhplpB/8i03au Fxkme6L8ee1S0oXqSjPTgw7JwOJXtkz6OPzt9wWN7nwBJSXxYVg6/sQH+HmUi0hgTIQV B0M/CPtisx6nAzRKZUosDEZNODE5E7V23tYlPhrmdgQa0zF8rDZWMeb4KNLgW+PfSVRD SYXg== X-Gm-Message-State: AMCzsaVxXXxxI33FFA4/9kg2KcIluCMDKr/mu7xwLK/VfTXnWJQJBxsf ALbKjNHlvHPmvgyejs+BGjU= X-Google-Smtp-Source: AOwi7QD8gWEpKI0uefRXf308VZl6kvdAMmAkdj57+nQaoJGnmbOq3KE540HPqVH46uPz8l+v1/Th2A== X-Received: by 10.223.139.18 with SMTP id n18mr1623983wra.223.1507820958168; Thu, 12 Oct 2017 08:09:18 -0700 (PDT) Original-Received: from lolita.yourcompany.com (188.139.62.94.rev.vodafone.pt. [94.62.139.188]) by smtp.gmail.com with ESMTPSA id 17sm20317059wru.32.2017.10.12.08.09.16 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Thu, 12 Oct 2017 08:09:17 -0700 (PDT) X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:400c:c09::22e X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:219418 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Hi, Here's a proposal for supporting Flymake in C/C++. This patch: - Sets up Flymake in c-mode buffers (heads up Alan), but doesn't automatically enable it. - Adds a special target to src/Makefile so that Flymake can work with Emacs's C sources (using a default cc-flymake-use-special-make-target method). - The special target's name is 'check-syntax' and uses CHK_SOURCES, which lets older Emacsen edit new Emacs sources with old Flymake implementations (dubious but harmless advantage). - Is customizable by the user to use a zero-configuration method that guesses GCC flags from Makefiles (see cc-flymake-use-cc-directly). This probably works in the simplest scenarios(*) - Is programmable by the user to use any other self-configuration technique (this includes per-file/dir manual configuration using a file-local cc-flymake-command) *: Sadly, GNU Hello no longer works since it uses a backquoted shell expression that the current implementation can't intercept (my old use-emacs-as-a-shell-parser ) Here it is, for reference gcc -DLOCALEDIR=3D\"/usr/local/share/locale\" -DHAVE_CONFIG_H -I. -I. -I.. = -I. -I. -I.. -I../intl -I../intl -g -O2 -c `test -f 'hello.c' || echo '.= /'`hello.c Also, FTR, discovered that -M flags do not seem to harm syntax-checking. No idea if dependencies files are still created anyway though. Jo=C3=A3o --=-=-= Content-Type: text/x-diff; charset=utf-8 Content-Disposition: inline; filename=0001-Add-a-Flymake-backend-for-C.patch Content-Transfer-Encoding: quoted-printable >From 41ef8318c8c539e475080e3b240b9c941d39caa9 Mon Sep 17 00:00:00 2001 From: =3D?UTF-8?q?Jo=3DC3=3DA3o=3D20T=3DC3=3DA1vora?=3D Date: Thu, 12 Oct 2017 14:04:51 +0100 Subject: [PATCH] Add a Flymake backend for C(++) * lisp/progmodes/cc-flymake.el: New file. * lisp/progmodes/cc-mode.el (c-mode): Call c--setup-flymake. (c--setup-flymake): New function. * src/Makefile.in: Add check-syntax target. --- lisp/progmodes/cc-flymake.el | 214 +++++++++++++++++++++++++++++++++++++++= ++++ lisp/progmodes/cc-mode.el | 9 ++ src/Makefile.in | 5 + 3 files changed, 228 insertions(+) create mode 100644 lisp/progmodes/cc-flymake.el diff --git a/lisp/progmodes/cc-flymake.el b/lisp/progmodes/cc-flymake.el new file mode 100644 index 0000000000..4baffdd3be --- /dev/null +++ b/lisp/progmodes/cc-flymake.el @@ -0,0 +1,214 @@ +;;; cc-flymake.el --- Flymake backends for C/C++ -*- lexical-binding: = t; -*- + +;; Copyright (C) 2017 Free Software Foundation, Inc. + +;; Author: Jo=C3=A3o T=C3=A1vora +;; Keywords: languages, c + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; Flymake support for C/C++. + +;;; Code: + +(require 'subr-x) ; when-let* + +(defcustom cc-flymake-command 'cc-flymake-use-special-make-target + "Command used by the CC flymake backend. +A list of strings, or a symbol naming a function that produces one +such list when called with no arguments in the buffer where the +variable `flymake-mode' is active. + +The command should invoke a GNU-style compiler that checks the +syntax of a (Obj)C(++) program passed to it via its standard +input and prints the result on its standard output." + :type '(choice + (symbol :tag "Function") + ((repeat :) string)) + :group 'cc-flymake) + +(defvar-local cc-flymake--cached-flags nil) + +(defun cc-flymake--guess-flags (cc-program &optional trash-cache) + "Guess C(++) flags for compiling current buffer. +Do this with by finding a suitable Makefile and intercepting an +invocation of CC-PROGRAM, a string naming a C(++) compiler, in +the output of \"make --just-print \". + +Cache the result. TRASH-CACHE of a change to the Makefile rests +the cache. + +This function signals an error if it encounters any problems, +which normally causes using backends to be disabled. + +Can also function interactively for debug purposes." + (interactive (list (read-from-minibuffer "Compiler? ") + current-prefix-arg)) + (when trash-cache (setq cc-flymake--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)))) + (cond + ((equal (list cc-program makefile mtime) + (cdr cc-flymake--cached-flags)) + (when (called-interactively-p 'interactive) + (message "cache HIT for %s flags: %S" cc-program + (car cc-flymake--cached-flags))) + (throw 'retval (car cc-flymake--cached-flags))) + (t + (let* + ((sans-nothing + (file-name-nondirectory + (file-name-sans-extension + (buffer-file-name)))) + (blob (shell-command-to-string + (format "make -C %s -f %s --just-print %s.o" + makefile-dir + makefile + sans-nothing))) + (match (string-match + (format "%s[[:space:]]+\\(\\(?:-.*\\)*\\)%s" + cc-program + sans-nothing) + blob)) + (flag-string (and match + (match-string 1 blob))) + (flags (and flag-string + ;; FIXME: shell unescaping: Nothing here to + ;; deal with simple backslash-escaped spaces, + ;; like quoted and backquoted expressions, + ;; etc. + (split-string + flag-string + nil + nil + "[[:space:]]+")))) + (when (or flags (string=3D "" flag-string)) + (setq cc-flymake--cached-flags + (list flags cc-program makefile mtime)) + (when (called-interactively-p 'interactive) + (message "cache MISS for %s flags: %S" cc-program flags)) + (throw 'retval flags)))))) + (error "Could not guess %s flags" cc-program))) + +(require 'compile) +(defun cc-flymake--make-diagnostics (source) + "Parse the current buffer of compilation messages. +Return a list of diagnostics for the source buffer SOURCE." + ;; TODO: if you can understand it, use `compilation-mode's regexps + ;; or even some of its machinery here. + ;; + ;; (set (make-local-variable 'compilation-locs) + ;; (make-hash-table :test 'equal :weakness 'value)) + ;; (compilation-parse-errors (point-min) (point-max) + ;; 'gnu 'gcc-include) + ;; (while (next-single-property-change 'compilation-message) + ;; ...) + ;; + ;; For now, this works minimaly well. + (cl-loop + while + (search-forward-regexp + "^\\(In file included from \\)?:\\([0-9]+\\):\\([0-9]+\\):\n?\\= (.*\\): \\(.*\\)$" + nil t) + for msg =3D (match-string 5) + for (beg . end) =3D (flymake-diag-region + source + (string-to-number (match-string 2)) + (string-to-number (match-string 3))) + for type =3D (if (match-string 1) + :error + (assoc-default + (match-string 4) + '(("error" . :error) + ("note" . :note) + ("warning" . :warning)) + #'string-match)) + collect (flymake-make-diagnostic source beg end type msg))) + +(defun cc-flymake-use-special-make-target () + "Build command for checking a file with Make directly." + (unless (executable-find "make") (error "Make not found")) + `("make" "check-syntax" "CHK_SOURCES=3D-x c -")) + +(defvar-local cc-flymake-program "cc" + "C(++) compiler used by `cc-flymake-use-cc-directly'.") + +(defun cc-flymake-use-cc-directly () + "Build command for checking a file with a C(++) compiler." + (unless (executable-find cc-flymake-program) + (error "%s not found" cc-flymake-program)) + `(,cc-flymake-program + "-fsyntax-only" + ,@(cc-flymake--guess-flags cc-flymake-program) + "-x" "c" "-")) + +(defvar-local cc-flymake--proc nil + "Internal variable for `flymake-gcc'") + +;;;###autoload +(defun cc-flymake (report-fn &rest _args) + "Flymake backend for GNU-style C compilers. +This backend uses `cc-flymake-command' (which see) to launch a +process that is passed the current buffer's contents via stdin. +REPORT-FN is Flymake's callback." + (when (process-live-p cc-flymake--proc) + (kill-process cc-flymake--proc)) + (let ((source (current-buffer))) + (save-restriction + (widen) + (setq + cc-flymake--proc + (make-process + :name "gcc-flymake" + :buffer (generate-new-buffer "*gcc-flymake*") + :command (if (symbolp cc-flymake-command) + (funcall cc-flymake-command) + cc-flymake-command) + :noquery t :connection-type 'pipe + :sentinel + (lambda (p _ev) + (when (eq 'exit (process-status p)) + (unwind-protect + (when (eq p cc-flymake--proc) + (with-current-buffer (process-buffer p) + (goto-char (point-min)) + (let ((diags + (cc-flymake--make-diagnostics source))) + (if (or diags + (zerop (process-exit-status p))) + (funcall report-fn diags) + ;; non-zero exit with no diags is cause + ;; for alarm + (funcall report-fn + :panic :explanation + (buffer-substring + (point-min) (progn (goto-char (point-min= )) + (line-end-position)))= ))))) + ;; (display-buffer (process-buffer p)) ; for debug + (kill-buffer (process-buffer p))))))) + (process-send-region cc-flymake--proc (point-min) (point-max)) + (process-send-eof cc-flymake--proc)))) + +(provide 'cc-flymake) +;;; cc-flymake.el ends here diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index b0e5fe47a7..37c8ecfa98 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -1842,6 +1842,7 @@ c-mode (c-common-init 'c-mode) (easy-menu-add c-c-menu) (cc-imenu-init cc-imenu-c-generic-expression) + (c--setup-flymake) (c-run-mode-hooks 'c-mode-common-hook)) =20 (defconst c-or-c++-mode--regexp @@ -2297,6 +2298,14 @@ c-submit-bug-report (insert (format "Buffer Style: %s\nc-emacs-features: %s\n" style c-features))))))) =20 +(defun c--setup-flymake () + "Setup flymake for cc buffers." + (add-hook 'flymake-diagnostic-functions 'cc-flymake nil t) + (defvar flymake-proc-allowed-file-name-masks) + ;; hackinly convince the legacy `flymake-proc' backend to disable + ;; itself. + (setq-local flymake-proc-allowed-file-name-masks nil)) + (cc-provide 'cc-mode) =20 diff --git a/src/Makefile.in b/src/Makefile.in index 9a8c9c85f0..66c259902f 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -746,3 +746,8 @@ bootstrap-emacs$(EXEEXT): endif @: Compile some files earlier to speed up further compilation. $(MAKE) -C ../lisp compile-first EMACS=3D"$(bootstrap_exe)" + +### Flymake support (for C only) +check-syntax: + $(AM_V_CC)$(CC) -c $(CPPFLAGS) $(ALL_CFLAGS) ${CHK_SOURCES} || true +.PHONY: check-syntax --=20 2.11.0 --=-=-=--