6

I have a project that wants to play a sound file. I'd like to distribute this project in MELPA. There are two issues I'm having with that. They're both part of the question "how do I use resources in an Emacs package", so I'm asking this as one question.

1. Package setup

How do I set it up so I can distribute it through ELPA? Obviously the sound file needs to be included. Is there anything I need to do in the package, or is that something that needs to happen upon someone creating the package download?

2. Referencing the file

I'm having a problem referencing the sound file from the Emacs Lisp file. There's the function expand-file-name, but if I open a new Emacs and load-file the elisp file, during the call to expand-file-name for the sound file, the path is expanded relative to the location of the Emacs executable.

There's an argument to expand-file-name that is the default directory to start with, but I'm not sure how to get the directory the file is in when the file is being loaded. During the execution of load-file, default-directory gets the value of the folder that holds the Emacs executable. How can I get the file to load without hardcoding the full path?

zck
  • 8,984
  • 2
  • 31
  • 65
  • 1
    https://www.gnu.org/software/emacs/manual/html_node/elisp/Multi_002dfile-Packages.html provides some infos on this topic. – xuchunyang Nov 09 '15 at 05:44

1 Answers1

4

Emacs supports multifile packages, which are essentially just TAR archives that contain arbitrary files, plus some package metadata. However, MELPA builds these archives for you, so you don't need to concern yourself with the actual details of the package format. Just add your resource files to the MELPA recipe of your package—take a look at flycheck-haskell for instance, which contains additional Haskell source files by the *.hs glob in :files.


Unfortunately Emacs does not provide an API for packages to load their resources. Fortunately, though, Emacs' package format is somewhat simple: An installed package is essentially just the tarball being extracted to disk.

Hence, you can load your resources relative to your Emacs Lisp code. Emacs provides a special variable load-file-name, which contains the full path of the file currently being loaded.

You can use this variable to automatically determine the directory where your package is located:

(defconst my-package-directory (file-name-directory load-file-name))

Later in your code you can load resources relative to my-package-directory:

(play-sound (expand-file-name "resources/my-sound-file.mp3" my-package-directory))

This loads the file resources/my-sound-file.mp3 from your package.

Note, though, that you cannot use load-file-name inside functions. You must use it on a top-level form, because it's only defined while the package is being loaded, i.e. while it's top-level forms are evaluated.