2

I'd like to create a Emacs Lisp macro that is able to generate a string literal that contains the name of the .el file into which the macro is expanded. Is this possible?

Background:

I am writing a Elisp macro that generate one or several defun forms. Some may be inside conditional forms (if or conditions or whatever) and the macro being expanded may also be inside a conditional block form.

Assuming I have the following code inside a file called my-file.el:

(when some-condition
    (my-macro lisp arg1 arg2))

I'd like to be able to write a defmacro form that would be able to determine that it is being expanded inside my-file.el and would be able to generate the string literal "my-file". Something like:

(when some-condition
   (defun my-lisp-function (some-arg arg1)
     (some-code-here arg1 whatever))
     (declare-function my-lisp-function "my-file")
   ;;
   (some-other-code etc etc)
   ;;
   (my-lisp-function arg2)) 

In the example above I'm using the file name string literal inside a declare-function form to prevent the byte-compiler from complaining about the generated my-lisp-function not being defined. And at the same time allow the check-declare package to verify the existence of the function.

I tried to use the variable byte-compile-current-file from bytecomp.el by accessing it in the non-generated portion of the macro, but that did not work.

1 Answer 1

3

I'd like to create a Emacs Lisp macro that is able to generate a string literal that contains the name of the .el file into which the macro is expanded. Is this possible?

If I understood correctly, Emacs 28 adds a function which does this:

(defun macroexp-file-name ()
  "Return the name of the file from which the code comes.
Returns nil when we do not know.
A non-nil result is expected to be reliable when called from a macro in order
to find the file in which the macro's call was found, and it should be
reliable as well when used at the top-level of a file.
Other uses risk returning non-nil value that point to the wrong file."
  ;; `eval-buffer' binds `current-load-list' but not `load-file-name',
  ;; so prefer using it over using `load-file-name'.
  (let ((file (car (last current-load-list))))
    (or (if (stringp file) file)
        (bound-and-true-p byte-compile-current-file))))

It follows that some key variables of interest are:

  • current-load-list
  • load-file-name
  • byte-compile-current-file
  • buffer-file-name

Sample usage:

(defmacro my-foo ()
  "Echo name of file where this macro is expanded or nil."
  `(message "%s" ,(macroexp-file-name)))
6
  • Thanks! That looks like what I need. I'm not using Emacs 28 yet. I'll have to try it on that. I assume that it means that the functionality is not available before Emacs 28.
    – PRouleau
    Commented Apr 13, 2021 at 21:21
  • Great! But can you please show how that function is used, in the OP's example or otherwise? Its doc string isn't very helpful: "the file from which the code comes". What code? And the doc strings for the two variables used in that code are even worse: the first says only "Used for internal purposes by `load'.", and the second doesn't exist (no doc).
    – Drew
    Commented Apr 13, 2021 at 21:33
  • @PRouleau Right, this function does not exist before Emacs 28. But you can inline or copy its definition if you need to. The idea is to use one of current-load-list, load-file-name, byte-compile-current-file, or buffer-file-name, roughly in that order.
    – Basil
    Commented Apr 13, 2021 at 21:48
  • @Basil. Yeah, you're right. I'll try that now.
    – PRouleau
    Commented Apr 13, 2021 at 21:49
  • @Drew I've given a simple example, but the rest of your comment seems like something to take up with the OP and bug-gnu-emacs.
    – Basil
    Commented Apr 13, 2021 at 21:52

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.