0

Problem

After executing the following block of "minimal reproducible example" code, helm-mode is not defined thus leading to an error. I have run into these kinds of errors before when something is not loaded, however that should not be the case since require did successfully complete for Helm. Clearly, there must be more to using/requiring/loading/etc. a package from a local Git repository (without resorting to fanciful features/external solutions) than ensuring a valid load-path and requiring dependencies in the right order.

Context / Setup

I need to fully and completely (interpret that however appropriate) use/require/load/install/etc. a package from a local Git repository. I have all of the dependencies for any given package in similar local Git repositories. I have added all of the Git repositories (and sub-directories/etc.) to the load-path list in Emacs before calling require on anything. I verified they're the correct paths, they're added to the load-path, directory spelling, etc.

For the sake of simplicity, this question focuses on a single package as the example: helm

Minimal reproducible example code:

; init.el
(setq-default package-enable-at-startup nil) ; Stop package.el from interfering
(add-to-list 'load-path "/Users/david/.emacs.d/my_repos/helm/")
(add-to-list 'load-path "/Users/david/.emacs.d/my_repos/async/")
(add-to-list 'load-path "/Users/david/.emacs.d/my_repos/wfnames/")
(add-to-list 'load-path "/Users/david/.emacs.d/my_repos/popup/")
(add-to-list 'load-path "/Users/david/.emacs.d/my_repos/all-the-icons/")
; NOTE: - Assume that other repositories and sub-directories are added here as well ... omitted for brevity. (Yes, before anyone asks, I confirmed they indeed are all in the `load-path`)
(require 'async) ; Dependency
(require 'wfnames) ; Dependency
(require 'popup) ; Dependency
(require 'all-the-icons) ; Dependency
; NOTE: - Assume they all pass and do not throw any errors/fail
(require 'helm)
(helm-mode) ; This should enable Helm or at the very least not throw an error but it does: Symbol's function definition is void: helm-mode

Preempting clarifying questions: Yes, this was the only thing in my init.el file (and yes, I made sure early-init.el was not loaded/interfering).

I also ran it via the following command as well (with the same result):

emacs -Q --load /Users/david/.emacs.d/temp/init_file_tests/helm_debug_init.el

Constraints & Disclosures

There are some additional constraints that I want to clarify since most answers I have found during my research generally fallback to something that violates one of these constraints:

  • Cannot use MELPA or any ELPA repository
  • Cannot use straight.el or any package manager
  • Cannot compile the .el files in any repository
  • Cannot use or rely on any lazy loading of anything (i.e. everything should always be loaded/the code does what it literally says)
  • Cannot setup any kind of internal (or local) ELPA server
  • Cannot host any kind of server or infrastructure
  • Cannot access/modify anything outside of ~/.emacs.d
  • Preferred to use only require for loading files
  • Performance or impacts to performance have absolutely no bearing or importance at all (I don't care if things take 0 seconds or 50 minutes)

Standard environment disclosure:

  • OS: MacOS
  • Chip: Intel
  • Emacs Version: 27.2.1
  • Port: Mitsuharu Yamamoto / Railwaycat
  • Other Notes: - No special distributions / etc.
  • Relevant Common Packages Used:
    • Standard internal Emacs packages

Why / Supplemental Details

Simply put, I want to have a completely self-contained, fully-isolated Emacs configuration that lives in a Git repository (with sub Git repositories for "packages") that can be cloned/used anywhere and used without relying on having an Internet connection, firewall access, admin/root access, installed tool-chains, etc. As well, any of the package's code can be easily and freely modified at runtime without any need for recompiling or external steps. I often work in environments where 1 or more of those limits are imposed without any possibility of exceptions being made or workarounds.

Additionally, avoiding solutions that are overly robust and featureful (e.g. straight.el, etc.) is critical because I don't want any "magic" or "vendor" lock-in to API's/etc. I am sure the solutions are awesome, brilliant, marvels of software engineering but they simply are orders of magnitude more complex than what is needed for my use case. Super simple (if even stupidly naive) is the best choice here.

3
  • Looking at this now, in more detail, I think it's because of the autoloads. require doesn't fully require/load everything as some files aren't in the require-dependency-graph (if you will), instead, they're relying on autoload to have key functions loaded (i.e. helm-mode is autoload'ed but never require'ed anywhere in the normal first require-dependency-graph of calls) ... I think
    – David
    Nov 20, 2023 at 4:15
  • FYI, using the package manager does not in any way prevent you committing your complete config to git, and nor do you need an internet connection after the package manager has installed the packages. You can trivially install packages with package-install, commit the installed packages, and then re-use your committed config anywhere -- how you acquired the files is not relevant. The lisp files will be byte-compiled, but C-h v load-prefer-newer allows you to not care about that. (Maybe setting the global value of no-byte-compile inhibits that, if you're determined.)
    – phils
    Nov 20, 2023 at 4:35
  • 1
    Looking at the sources on GitHub, helm.el does not require helm-mode.el, so it's not surprising that helm-mode is not defined. OTOH, helm-mode.el does require helm, so just (require 'helm-mode) instead of (require 'helm) on the penultimate line.
    – NickD
    Nov 20, 2023 at 4:36

1 Answer 1

0

The only thing you’re missing is that you didn’t follow the directions for loading Helm. The directions say to add this to your init file:

(add-to-list 'load-path "/path/to/helm/directory") ; facultative when installed with make install
(require 'helm)
(require 'helm-autoloads)

That is, first load the helm package followed by the helm-autoloads package.

The wrinkle is that the helm-autoloads package is not part of the source code; it is generated from it. You should run make autoloads in Helm’s source directory to generate it. You only need to do that once, and then you’ll be good to go.

2
  • There is no need to be rude. I noted in my original question that Helm was just an example. We can see that I implied/noted in my OP, that I had read the doc and the solution I would need can't rely on having toolchains/compilers available or access to root directories like /usr/.... I did mention in my follow-up comment that I believed autoload's were the culprit. To avoid rewrites, I did some digging into Emacs' source code to find out that there is a method to use for creating autoloads on the fly: update-directory-autoloads that needs a fair bit of massaging to work.
    – David
    Nov 20, 2023 at 20:27
  • Yes, that is how the autoloads are created. Just run that once and commit it to your repository.
    – db48x
    Nov 21, 2023 at 0:54

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.