[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bug#73681: Maybe partly undo the patch on Elisp comp-el-to-eln-filename
From: |
Martin Edström |
Subject: |
bug#73681: Maybe partly undo the patch on Elisp comp-el-to-eln-filename |
Date: |
Wed, 09 Oct 2024 17:15:54 +0200 (CEST) |
Hi, thanks for info! This email will be a bit long because I'm quite confused
and thinking aloud, so no need to reply to all of it. But I appreciate any
attempt to shed clarity!
On Tue, 08 Oct 2024 19:33:06 +0200, Liliana Marie Prikler
<liliana.prikler@gmail.com> wrote:
> > It comes as part of the package. I don't want to assume that it has
> > been compiled, since it's fairly performance-sensitive. That's why
> > I'll either use a previously existing compiled object or make a new
> > one.
> Could you leave that decision to the user?
I'm considering it, but ran into essentially the same problem. I need a way to
tell which one was loaded, of the .elc, .eln or .el.
This takes the thread a bit off-topic but ... Do you know how?
Currently I'm leaning towards an algorithm like
#+begin_src elisp
(defun guess-loaded-lib (basename)
(let* ((el (locate-library (concat basename ".el")))
(elc (locate-library (concat basename ".elc")))
(eln (comp-el-to-eln-filename el)))
(if load-prefer-newer
(car (sort (delq 'nil (list el elc eln)) #'file-newer-than-file-p))
(if (and (file-newer-than-file-p elc el)
(file-newer-than-file-p eln el))
;; If elc is newer than el, assume it was compiled from el, and
;; therefore is safe to replace with eln
eln
elc))))
#+end_src
I don't know how to "leave it to the user" any better than that.
On Guix, even in the future when we re-enable JIT compilation, this algorithm
could never return an .eln, since =file-newer-than-file-p= returns nil when
both paths have identical timestamps from 1970.
I found a pretty neat built-in:
(symbol-file 'SOME-FUNCTION-FROM-THE-LIB nil t)
but its internals also use =file-newer-than-file-p= and
=comp-el-to-eln-rel-filename=, so not sure it is any more reliable than what I
have above.
> There is a separate load path for natively compiled files, called
> `native-comp-eln-load-path'.
Good to know! I don't know how Emacs consults it though. I can't e.g. locate
an .eln of a given library by calling something like
#+begin_src elisp
(locate-eln-file "org-node-parser")
#+end_src
so it seems I must simply do:
#+begin_src elisp
(let ((el (locate-library "org-node-parser.el")))
(when el
(locate-eln-file (comp-el-to-eln-rel-filename el))))
#+end_src
which is functionally equivalent to:
#+begin_src elisp
(let ((eln (comp-el-to-eln-filename (locate-library "org-node-parser.el"))))
(when (file-exists-p eln)
eln))
#+end_src
> > I infer that Emacs starts with finding a library in load-path, then
> > converts the path with `comp-el-to-eln-filename`, and checks if that
> > file exists, then loads it.
> >
> > And crucially, it is not just about the filepath, the function hashes
> > the file contents as well. That ensures that the output path is
> > always different if the source file changes.
> I think relying on such implementation details is perhaps permitted if
> it's inside of Emacs itself, but even then it clashes with our
> expectation that Emacs be graftable.
OK, so if passing "file.el" to =comp-el-to-eln-filename= produces a path with a
hash of the content, like:
.../eln-cache/29.4-HASH/module/file-HASH-BASED-ON-CONTENT.eln
this would make Emacs non-graftable. Because the hash would change after
file.el is upgraded. I suppose that makes sense, thanks!
Maybe, as a hack, the graft process could symlink the pre-graft filename to the
post-graft filename... so we don't have to alter the behavior of
=comp-el-to-eln-filename=.
But yea... it's less dev effort to just not pre-compile any .eln files.
Out of curiosity: if Guix does no JIT compilation at all anyway, why does it
not let Emacs do it the usual way into ~/.emacs.d/eln-cache, post-installation?
(Aside from the fact it would necessitate reverting the behavior of
=comp-el-to-eln-filename=.)
Actually... (with reservation that I may be wasting your time) I'm starting to
wonder why the filename needs to stay the same for Emacs to be graftable?
Realistically, if all Emacs packages get upgraded, and if
=comp-el-to-eln-filename= works like upstream, we have some paths like
/gnu/store/emacs-29.4/bin/emacs
/gnu/store/emacs-magit-OLD/lisp/magit.el
/gnu/store/emacs-magit-NEW/lisp/magit.el
.../wherever/magit-OLDHASH.eln
.../wherever/magit-NEWHASH.eln
Then we start Emacs, and run its =comp-el-to-eln-filename= on the new magit.el,
it would correctly return .../wherever/magit-NEWHASH.eln. No need for static
filenames to swap out.
Is graftability just about the compiled objects inside of
/gnu/store/emacs-29.4/share/emacs/site-lisp/ that might be symlinked in from
other Guix packages?
If so, I guess it ties back to my confusion about how Emacs uses the
native-comp-eln-load-path, because it seems that IF it just converted an .el to
find an .eln as I thought, then grafting would work automatically because the
new .el is also symlinked in along with the new .eln, which would result in
=comp-el-to-eln-filename= returning the new .eln correctly.
No, grafting is about avoiding a world re-compile, right? So like if package-A
was built with an old dependency package-B, it does not get re-built when
package-B is upgraded. But does grafting actually ever change anything in
Emacs? When you start emacs and it loads package-A which in turn loads
package-B because there's a =require= statement for package-B, Emacs will
actually load the fresh package-B from /gnu/store/emacs-package-B, won't it?
So grafting would seem to be meaningless.
Not sure how to feel if it's like this instead: a compiled function from
package A, containing a call to some function "package-B-frobnicate", will
actually call some bytecode originating from
/gnu/store/emacs-package-A/share/emacs/site-lisp/package-B.elc? That's... not
what happens, right? OK, probably yes if it was a defsubst or defmacro.
So we can have package A using a macro that was defined in an old version of
package-B. I like that not, but the reason I ask is I'm trying to find out
where it is that the file path would influence the graft.
> The way our load paths are set up, it is actually the opposite (which
> still is a bug, just not the one reported). While `guix upgrade` or a
> command to the similar effect will swap out the .eln under the hood,
> the `.el` and `.elc` files stay stable – remember what I wrote in the
> previous message about that having caused issues with byte compilation?
I confess I'm puzzled. Does this just apply to a case like the following?
- Emacs itself is upgraded
- A package emacs-magit is NOT upgraded
so that
- magit.eln is rebuilt
- magit.el and magit.elc stay the same
That would make sense. Do you mean it's a bug because .elc files are also not
portable between different Emacsen?
Just checking: is it correct to expect that if you actually upgrade the
emacs-magit package, then its .el, .elc and .eln are all upgraded?
- bug#73681: Maybe partly undo the patch on Elisp comp-el-to-eln-filename, Martin Edström, 2024/10/07
- bug#73681: Maybe partly undo the patch on Elisp comp-el-to-eln-filename, Liliana Marie Prikler, 2024/10/07
- bug#73681: Maybe partly undo the patch on Elisp comp-el-to-eln-filename, Martin Edström, 2024/10/07
- bug#73681: Maybe partly undo the patch on Elisp comp-el-to-eln-filename, Liliana Marie Prikler, 2024/10/08
- bug#73681: Maybe partly undo the patch on Elisp comp-el-to-eln-filename, Martin Edström, 2024/10/08
- bug#73681: Maybe partly undo the patch on Elisp comp-el-to-eln-filename, Liliana Marie Prikler, 2024/10/08
- bug#73681: Maybe partly undo the patch on Elisp comp-el-to-eln-filename,
Martin Edström <=
- bug#73681: Maybe partly undo the patch on Elisp comp-el-to-eln-filename, Liliana Marie Prikler, 2024/10/09
- bug#73681: Maybe partly undo the patch on Elisp comp-el-to-eln-filename, Martin Edström, 2024/10/18