0

I recently refactored my Emacs config, part of which involved deleting some old files which had been byte-compiled.

My new configuration (ab)uses the load-dir package to load all my customisation files in a specific directory. The issue arises when I update my config on a different machine, those old *.elc files are still there and loaded by load-dir, causing confusion as various settings are being overridden by *.elc files.

Is there any way to tell Emacs to only consider loading a .elc file if a corresponding .el file exists?

5
  • Why are you loading all files in a directory, instead of loading those you want to load?
    – Drew
    Commented Aug 8, 2017 at 17:02
  • @Drew so I don't have to explicitly load them all; I came across the trick whilst I was migrating over to using req-package.
    – ivanm
    Commented Aug 9, 2017 at 9:55
  • Does the set of files change much? If not, M-x list-directory gives you an editable list, which you can easily change to require or load-library calls. (Anyway, this doesn't answer your question - I was just wondering.)
    – Drew
    Commented Aug 9, 2017 at 14:02
  • Not really, and even now I can still always manually delete all *.elc files and get Emacs to re-byte-compile them all. I suppose I asked this question more out of curiousity as to if it was possible rather than needing a solution.
    – ivanm
    Commented Aug 10, 2017 at 4:03
  • Deleting all .elc files seems excessive. You can list all the .elc files which do not have a corresponding .el file with something like find . -regex ".*.elc?" | sed 's/\.elc\?//' | sort | uniq -u | sed 's/$/.elc/' | xargs find 2>/dev/null
    – phils
    Commented Dec 22, 2017 at 9:41

1 Answer 1

1

Is there any way to tell Emacs to only consider loading a .elc file if a corresponding .el file exists?

I am not sure whether this exact option exists, but the Emacs 24.4+ variable load-prefer-newer comes close:

load-prefer-newer is a variable defined in ‘C source code’.

Its value is nil

Documentation:

Non-nil means load prefers the newest version of a file. This applies when a filename suffix is not explicitly specified and load is trying various possible suffixes (see load-suffixes and load-file-rep-suffixes). Normally, it stops at the first file that exists unless you explicitly specify one or the other. If this option is non-nil, it checks all suffixes and uses whichever file is newest. Note that if you customize this, obviously it will not affect files that are loaded before your customizations are read!

You can customize this variable.

This variable was introduced, or its default value was changed, in version 24.4 of Emacs.

Setting this to t will allow you to place newer .el files alongside obsolete .elc files and Emacs will prefer the former. As the documentation warns, this setting must be changed before any of the relevant files are loaded. So, if you want the setting to apply to your user-init-file, you must enable it before this is loaded, i.e. in your site-run-file or similar.

Another solution to this loading problem is provided by the package auto-compile in the form of auto-compile-on-load-mode. Enabling this minor mode causes outdated .elc files to be automatically recompiled when they are loaded.

Update

Further to OP's comment below, here is one possible approach to solving the issue by advising require/load:

(defun my--locate-file (base suffix)
  "Search for BASE filename with SUFFIX in `load-path'.
Representations of SUFFIX under `load-file-rep-suffixes' are
tried successively until one succeeds."
  (locate-file-internal base load-path (mapcar (lambda (rep)
                                                 (concat suffix rep))
                                               load-file-rep-suffixes)))

(define-advice require (:before-while (feature &optional filename _noerror)
                                      my-ignore-obsolete-compiled)
  "Do not delegate an obsolete compiled FILENAME to `require'.
FILENAME is considered obsolete if it is byte-compiled but lacks
a source file."
  (let ((base (file-name-base (or filename (symbol-name feature)))))
    (or (not (my--locate-file base ".elc"))
        (my--locate-file base ".el"))))

Note that this affects nearly all file loading in Emacs, so if left non-byte-compiled it may even double your startup time. You can mitigate this by byte-compiling your user-init-file and/or further guarding the calls to my--locate-file, e.g. with a FEATURE/FILENAME whitelist or similar.

Also note that define-advice is an Emacs 25 feature - in Emacs 24 you should adapt the code to call advice-add directly instead.

2
  • I already have load-prefer-newer but it doesn't work in the case of having a foo.elc without a corresponding foo.el there.
    – ivanm
    Commented Aug 9, 2017 at 9:56
  • @ivanm See my updated answer and let me know if the proposal helps at all.
    – Basil
    Commented Sep 22, 2017 at 19:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.