Reported by: Sebastian Wiesner <swiesner <at> lunaryorn.com>
Date: Fri, 28 Nov 2014 10:21:02 UTC
Severity: normal
Found in version 25.0.50
Done: Alan Mackenzie <acm <at> muc.de>
Bug is archived. No further changes may be made.
View this message in rfc822 format
From: Sebastian Wiesner <swiesner <at> lunaryorn.com> To: Alan Mackenzie <acm <at> muc.de> Cc: 19206 <at> debbugs.gnu.org Subject: bug#19206: 25.0.50; CC Mode tracks wrong source files Date: Tue, 2 Dec 2014 12:03:21 +0100
> Am 30.11.2014 um 19:42 schrieb Alan Mackenzie <acm <at> muc.de>: > > Hello, again, Sebastian. > > On Fri, Nov 28, 2014 at 10:25:42PM -0000, Alan Mackenzie wrote: >> Hello, Sebastian. >> In article <mailman.14863.1417170074.1147.bug-gnu-emacs <at> gnu.org> you wrote: >>> CC Mode tracks wrong source files when a CC Mode derived mode is >>> installed non-interactively. > >>> To reproduce, save the following code as `cc-miscompile.el' > >>> (require 'package) >>> (require 'cc-defs) > >>> (defun main () >>> (add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/")) > >>> (setq package-user-dir (make-temp-file "cc-miscompile" 'directory)) > >>> (package-initialize) >>> (package-refresh-contents) >>> (package-install 'd-mode) > >>> (require 'd-mode) > >>> (let ((source (get (intern "c-typedef-decl-kwds" c-lang-constants) 'source))) >>> (message "Sources: %S" (mapcar 'car source))) > >>> (delete-directory package-user-dir 'recursive)) > >>> (main) > >>> and run it with `emacs -Q --script cc-miscompile.el'. The output is as >>> follows (package.el output shortened for readility): > >>> Contacting host: melpa.org:80 >>> Contacting host: elpa.gnu.org:80 >>> [?] >>> Sources: (d-mode cc-miscompile cc-langs) > >>> Note that `cc-miscompile' ends up in the source list of >>> `c-typedef-decl-kwds', even though it never actually calls any `c-*' >>> functions at all. > > OK. The problem was that CC Mode was using the flag `load-in-progress' > to assume that a CC Mode file was being loaded, for example by a > `require' form inside a compilation of another CC Mode file. This > assumption breaks down when another file, such as cc-miscompile.el, > while loading, initiates compilation of a CC Mode derivative. > >> The byte compilation of d-mode.el is being done during the loading of >> cc-miscompile.el. This somewhat unusual constellation, I think, is >> causing the problem. When CC Mode determines the file name to put onto >> a c-lang-defconst's 'source property, it gives priority to the load file >> name, and only when this is nil does it use the byte-compile file name. >> (This is in defsubst c-get-current-file in cc-defs.el). It would seem >> this is not the correct priority. > >> I think swapping the first two arms of the `cond' form in >> c-get-current-file may solve the problem. It's a bit late to try this >> tonight, I'll try it tomorrow. > > No, that wouldn't work. What I've implemented is when both loading and > byte-compilation are active at the same time, CC Mode walks down the > lisp stack to discover which one of them is actually active. Well, if you say… I find this “solution” horrifying, but I am probably just unable to appreciate the full complexity of this issue. >>> Naturally, CC Mode will later try to load this file, and fail if it is >>> not in the `load-path'. This effectively breaks installations of D >>> Mode from non-interactive Emacs sessions. > > Please try the following patch, which seems to work in the test case you > supplied, in the real situation, and let me know how well it works. The > d-mode.elc produced can be successfully loaded into the Emacs that built > it. Do I need to build a patched Emacs to try it? > diff --git a/lisp/progmodes/cc-bytecomp.el b/lisp/progmodes/cc-bytecomp.el > index 1936627..bd2fd34 100644 > --- a/lisp/progmodes/cc-bytecomp.el > +++ b/lisp/progmodes/cc-bytecomp.el > @@ -84,13 +84,60 @@ > ;;`(message ,@args) > ) > > +(defun cc-bytecomp-compiling-or-loading () > + ;; Return whether byte-compilation or loading is currently active, > + ;; returning 'compiling or 'loading or nil. > + ;; If both are active, the "innermost" activity counts. Note that > + ;; compilation can trigger loading (various `require' type forms) > + ;; and loading can trigger compilation (the package manager does > + ;; this). We walk the lisp stack if necessary. > + (cond > + ((and load-in-progress > + (boundp 'byte-compile-dest-file) > + (stringp byte-compile-dest-file)) > + (let ((n 0) elt) > + (while (and > + (setq elt (backtrace-frame n)) > + (not (and (car elt) > + (memq (cadr elt) > + '(load byte-compile-file > + byte-recompile-directory > + batch-byte-compile))))) > + (setq n (1+ n))) > + (cond > + ((eq (cadr elt) 'load) > + 'loading) > + ((memq (cadr elt) '(byte-compile-file > + byte-recompile-directory > + batch-byte-compile)) > + 'compiling) > + (t ; Can't happen. > + (message "cc-bytecomp-compiling-or-loading: System flags spuriously set") > + nil)))) > + (load-in-progress > + ;; Being loaded. > + 'loading) > + ((and (boundp 'byte-compile-dest-file) > + (stringp byte-compile-dest-file)) > + ;; Being compiled. > + 'compiling) > + (t > + ;; Being evaluated interactively. > + nil))) > + > +(defsubst cc-bytecomp-is-compiling () > + "Return non-nil if eval'ed during compilation." > + (eq (cc-bytecomp-compiling-or-loading) 'compiling)) > + > +(defsubst cc-bytecomp-is-loading () > + "Return non-nil if eval'ed during loading. > +Nil will be returned if we're in a compilation triggered by the loading." > + (eq (cc-bytecomp-compiling-or-loading) 'loading)) > + > (defun cc-bytecomp-setup-environment () > ;; Eval'ed during compilation to setup variables, functions etc > ;; declared with `cc-bytecomp-defvar' et al. > - (if (not load-in-progress) > - ;; Look at `load-in-progress' to tell whether we're called > - ;; directly in the file being compiled or just from some file > - ;; being loaded during compilation. > + (if (not (cc-bytecomp-is-loading)) > (let (p) > (if cc-bytecomp-environment-set > (error "Byte compilation environment already set - \ > @@ -138,7 +185,7 @@ perhaps a `cc-bytecomp-restore-environment' is forgotten somewhere")) > (defun cc-bytecomp-restore-environment () > ;; Eval'ed during compilation to restore variables, functions etc > ;; declared with `cc-bytecomp-defvar' et al. > - (if (not load-in-progress) > + (if (not (cc-bytecomp-is-loading)) > (let (p) > (setq p cc-bytecomp-unbound-variables) > (while p > @@ -282,9 +329,7 @@ use within `eval-when-compile'." > `(eval-when-compile > (if (and (fboundp 'cc-bytecomp-is-compiling) > (cc-bytecomp-is-compiling)) > - (if (or (not load-in-progress) > - (not (featurep ,cc-part))) > - (cc-bytecomp-load (symbol-name ,cc-part))) > + (cc-bytecomp-load (symbol-name ,cc-part)) > (require ,cc-part)))) > > (defmacro cc-external-require (feature) > @@ -296,12 +341,6 @@ afterwards. Don't use within `eval-when-compile'." > (require ,feature) > (eval-when-compile (cc-bytecomp-setup-environment)))) > > -(defun cc-bytecomp-is-compiling () > - "Return non-nil if eval'ed during compilation. Don't use outside > -`eval-when-compile'." > - (and (boundp 'byte-compile-dest-file) > - (stringp byte-compile-dest-file))) > - > (defmacro cc-bytecomp-defvar (var) > "Binds the symbol as a variable during compilation of the file, > to silence the byte compiler. Don't use within `eval-when-compile'." > @@ -315,8 +354,7 @@ to silence the byte compiler. Don't use within `eval-when-compile'." > "cc-bytecomp-defvar: Saving %s (as unbound)" ',var) > (setq cc-bytecomp-unbound-variables > (cons ',var cc-bytecomp-unbound-variables)))) > - (if (and (cc-bytecomp-is-compiling) > - (not load-in-progress)) > + (if (cc-bytecomp-is-compiling) > (progn > (defvar ,var) > (set ',var (intern (concat "cc-bytecomp-ignore-var:" > @@ -344,8 +382,7 @@ at compile time, e.g. for macros and inline functions." > (setq cc-bytecomp-original-functions > (cons (list ',fun nil 'unbound) > cc-bytecomp-original-functions)))) > - (if (and (cc-bytecomp-is-compiling) > - (not load-in-progress)) > + (if (cc-bytecomp-is-compiling) > (progn > (fset ',fun (intern (concat "cc-bytecomp-ignore-fun:" > (symbol-name ',fun)))) > diff --git a/lisp/progmodes/cc-defs.el b/lisp/progmodes/cc-defs.el > index 1d8b8ab..b0e83f3 100644 > --- a/lisp/progmodes/cc-defs.el > +++ b/lisp/progmodes/cc-defs.el > @@ -1823,19 +1823,22 @@ system." > > (defvar c-lang-const-expansion nil) > > +;; Ugly hack to pull in the definition of `cc-bytecomp-compiling-or-loading` > +;; from cc-bytecomp to make it available at loadtime. This is the same > +;; mechanism used in cc-mode.el for `c-populate-syntax-table'. > +(defalias 'cc-bytecomp-compiling-or-loading > + (cc-eval-when-compile > + (let ((f (symbol-function 'cc-bytecomp-compiling-or-loading))) > + (if (byte-code-function-p f) f (byte-compile f))))) > + > (defsubst c-get-current-file () > ;; Return the base name of the current file. > - (let ((file (cond > - (load-in-progress > - ;; Being loaded. > - load-file-name) > - ((and (boundp 'byte-compile-dest-file) > - (stringp byte-compile-dest-file)) > - ;; Being compiled. > - byte-compile-dest-file) > - (t > - ;; Being evaluated interactively. > - (buffer-file-name))))) > + (let* ((c-or-l (cc-bytecomp-compiling-or-loading)) > + (file > + (cond > + ((eq c-or-l 'loading) load-file-name) > + ((eq c-or-l 'compiling) byte-compile-dest-file) > + ((null c-or-l) (buffer-file-name))))) > (and file > (file-name-sans-extension > (file-name-nondirectory file))))) > @@ -2073,9 +2076,7 @@ quoted." > (if (or (eq c-lang-const-expansion 'call) > (and (not c-lang-const-expansion) > (not mode)) > - load-in-progress > - (not (boundp 'byte-compile-dest-file)) > - (not (stringp byte-compile-dest-file))) > + (not (cc-bytecomp-is-compiling))) > ;; Either a straight call is requested in the context, or > ;; we're in an "uncontrolled" context and got no language, > ;; or we're not being byte compiled so the compile time > diff --git a/lisp/progmodes/cc-langs.el b/lisp/progmodes/cc-langs.el > index 68b2d62..22d78b5 100644 > --- a/lisp/progmodes/cc-langs.el > +++ b/lisp/progmodes/cc-langs.el > @@ -3252,10 +3252,7 @@ function it returns is byte compiled with all the evaluated results > from the language constants. Use the `c-init-language-vars' macro to > accomplish that conveniently." > > - (if (and (not load-in-progress) > - (boundp 'byte-compile-dest-file) > - (stringp byte-compile-dest-file)) > - > + (if (cc-bytecomp-is-compiling) > ;; No need to byte compile this lambda since the byte compiler is > ;; smart enough to detect the `funcall' construct in the > ;; `c-init-language-vars' macro below and compile it all straight > > > > -- > Alan Mackenzie (Nuremberg, Germany).
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.