From 30c5f5cdef8db72c007efecfc8436479631b45d0 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Tue, 13 Jan 2015 15:39:36 +0000 Subject: [PATCH] Allow compilation during loading of Modes derived from a CC Mode mode. Fixes debbugs#19206. cc-bytecomp.el (cc-bytecomp-compiling-or-loading): new function which walks the stack to discover whether we're compiling or loading. (cc-bytecomp-is-compiling): Reformulate, and move towards beginning. (cc-bytecomp-is-loading): New defsubst. (cc-bytecomp-setup-environment, cc-bytecomp-restore-environment): Use the above defsubsts. (cc-require-when-compile, cc-bytecomp-defvar) (cc-bytecomp-defun): Simplify conditionals. cc-defs.el (cc-bytecomp-compiling-or-loading): "Borrow" this function from cc-bytecomp.el. (c-get-current-file): Reformulate using the above. (c-lang-defconst): Prevent duplicate entries of file names in a symbol's 'source property. (c-lang-const): Use cc-bytecomp-is-compiling. cc-langs.el (c-make-init-lang-vars-fun): Use cc-bytecomp-is-compiling. --- lisp/ChangeLog | 26 ++++++++++++++++ lisp/progmodes/cc-bytecomp.el | 72 +++++++++++++++++++++++++++++++++---------- lisp/progmodes/cc-defs.el | 43 ++++++++++++++++---------- lisp/progmodes/cc-langs.el | 5 +-- 4 files changed, 108 insertions(+), 38 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 532a10a1b71..072af5b1b91 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,29 @@ +2015-01-13 Alan Mackenzie + + Allow compilation during loading of Modes derived from a CC Mode mode. + Fixes debbugs#19206. + + * progmodes/cc-bytecomp.el (cc-bytecomp-compiling-or-loading): new + function which walks the stack to discover whether we're compiling + or loading. + (cc-bytecomp-is-compiling): Reformulate, and move towards + beginning. + (cc-bytecomp-is-loading): New defsubst. + (cc-bytecomp-setup-environment, cc-bytecomp-restore-environment): + Use the above defsubsts. + (cc-require-when-compile, cc-bytecomp-defvar) + (cc-bytecomp-defun): Simplify conditionals. + + * progmodes/cc-defs.el (cc-bytecomp-compiling-or-loading): + "Borrow" this function from cc-bytecomp.el. + (c-get-current-file): Reformulate using the above. + (c-lang-defconst): Prevent duplicate entries of file names in a + symbol's 'source property. + (c-lang-const): Use cc-bytecomp-is-compiling. + + * progmodes/cc-langs.el (c-make-init-lang-vars-fun): Use + cc-bytecomp-is-compiling. + 2015-01-13 Stefan Monnier * emacs-lisp/eieio-core.el (eieio-defclass): Fix call to `defclass' diff --git a/lisp/progmodes/cc-bytecomp.el b/lisp/progmodes/cc-bytecomp.el index bf7803c85ca..b63eeb4c7a6 100644 --- a/lisp/progmodes/cc-bytecomp.el +++ b/lisp/progmodes/cc-bytecomp.el @@ -89,13 +89,60 @@ ;;`(message ,@args) ) +(defun cc-bytecomp-compiling-or-loading () + ;; Determine whether byte-compilation or loading is currently active, + ;; returning 'compiling, '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 require + byte-compile-file byte-recompile-directory + batch-byte-compile))))) + (setq n (1+ n))) + (cond + ((memq (cadr elt) '(load require)) + '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 - \ @@ -143,7 +190,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 @@ -287,8 +334,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))) + (if (not (featurep ,cc-part)) (cc-bytecomp-load (symbol-name ,cc-part))) (require ,cc-part)))) @@ -301,12 +347,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'." @@ -320,8 +360,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:" @@ -349,8 +388,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 2ea566a7a25..d0beab1d485 100644 --- a/lisp/progmodes/cc-defs.el +++ b/lisp/progmodes/cc-defs.el @@ -1983,19 +1983,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))))) @@ -2062,6 +2065,9 @@ constant. A file is identified by its base name." ;; language constant source definitions.) (c-lang-const-expansion 'call) (c-langs-are-parametric t) + (file (intern + (or (c-get-current-file) + (error "`c-lang-defconst' can only be used in a file")))) bindings pre-files) @@ -2121,9 +2127,14 @@ constant. A file is identified by its base name." ;; definitions for this symbol, to make sure the order in the ;; `source' property is correct even when files are loaded out of ;; order. - (setq pre-files (nreverse - ;; Reverse to get the right load order. - (mapcar 'car (get sym 'source)))) + (setq pre-files (mapcar 'car (get sym 'source))) + (if (memq file pre-files) + ;; This can happen when the source file (e.g. cc-langs.el) is first + ;; loaded as source, setting a 'source property entry, and then itself + ;; being compiled. + (setq pre-files (cdr (memq file pre-files)))) + ;; Reverse to get the right load order. + (setq pre-files (nreverse pre-files)) `(eval-and-compile (c-define-lang-constant ',name ,bindings @@ -2233,9 +2244,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 17d717e740f..4d16a9b9d33 100644 --- a/lisp/progmodes/cc-langs.el +++ b/lisp/progmodes/cc-langs.el @@ -3260,10 +3260,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 -- 2.11.4.GIT