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.
require
doesn't fully require/load everything as some files aren't in the require-dependency-graph (if you will), instead, they're relying onautoload
to have key functions loaded (i.e.helm-mode
isautoload
'ed but neverrequire
'ed anywhere in the normal first require-dependency-graph of calls) ... I thinkpackage-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, butC-h v load-prefer-newer
allows you to not care about that. (Maybe setting the global value ofno-byte-compile
inhibits that, if you're determined.)helm.el
does not requirehelm-mode.el
, so it's not surprising thathelm-mode
is not defined. OTOH,helm-mode.el
does requirehelm
, so just(require 'helm-mode)
instead of(require 'helm)
on the penultimate line.