From 4a8c572ba6a64be997be072b44ef5ff62674d820 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 21 Jun 2026 02:16:42 -0400 Subject: fix: load games-config via the malyon hook, not an autoload chain The previous deferral (03d8b587) autoloaded malyon to games-config, but games-config doesn't define malyon. It leaves the command to the malyon package, so M-x malyon loaded games-config, found malyon still undefined, and errored "Autoloading games-config.el failed to define function malyon". Emacs won't chain through a second autoload. malyon and 2048-game autoload their own commands via package.el, so games-config should never own them. init.el now loads games-config via (with-eval-after-load 'malyon ...), and games-config just sets malyon-stories-directory when malyon loads. M-x malyon loads the package as a real command, then games-config applies its config. The earlier batch check loaded the files by hand and missed the autoload failure. The new test resolves the autoload the way M-x does (autoload-do-load), so the real path is covered now. --- docs/design/module-inventory.org | 2 +- init.el | 8 ++++---- modules/games-config.el | 32 +++++++++++--------------------- tests/test-init-defer-games.el | 36 ++++++++++++++++++++++-------------- 4 files changed, 38 insertions(+), 40 deletions(-) diff --git a/docs/design/module-inventory.org b/docs/design/module-inventory.org index 3903d0ba9..fb883d701 100644 --- a/docs/design/module-inventory.org +++ b/docs/design/module-inventory.org @@ -205,7 +205,7 @@ flyspell-and-abbrev is the one Core-UX member (text-mode hooks). | =eshell-config= | 3 | D/P | eager | command | system-utils | add-hook, advice-add, package config | yes | | =eww-config= | 3 | D/P | eager | command | cl-lib | package config | yes | | =flyspell-and-abbrev= | 2 | C/P | eager | hook | cl-lib | mode-hook package config | yes | -| =games-config= | 4 | O | command | command | none | package config | yes | +| =games-config= | 4 | O | command | command | user-constants | package config | yes | | =gloss-config= | 4 | O/D/P | eager | command | none | package config | yes | | =httpd-config= | 4 | O/D/P | eager | command | none | package config | yes | | =jumper= | 4 | O/L | eager | command | cl-lib | jumper keymap | yes | diff --git a/init.el b/init.el index eab444d00..cda6aeb72 100644 --- a/init.el +++ b/init.el @@ -153,10 +153,10 @@ ;; ------------------------------- Entertainment ------------------------------- (require 'music-config) -;; games-config: deferred (load-graph Phase 4). malyon / 2048-game autoload the -;; module, so it loads on first use instead of at startup. -(autoload 'malyon "games-config" "Play interactive fiction; loads games-config." t) -(autoload '2048-game "games-config" "Play 2048; loads games-config." t) +;; games-config: deferred (load-graph Phase 4). malyon / 2048-game autoload +;; their own commands via package.el; games-config only supplies malyon's config, +;; so load it when malyon loads rather than requiring it at startup. +(with-eval-after-load 'malyon (require 'games-config)) ;; ------------------------------- Misc Modules -------------------------------- diff --git a/modules/games-config.el b/modules/games-config.el index 1e5ba5b87..aa26d31ee 100644 --- a/modules/games-config.el +++ b/modules/games-config.el @@ -6,37 +6,27 @@ ;; Layer: 4 (Optional). ;; Category: O. ;; Load shape: command (deferred). -;; Eager reason: none; loaded on first use of `malyon' or `2048-game'. -;; Top-level side effects: package configuration via use-package (deferred). -;; Runtime requires: none. +;; Eager reason: none; loaded by init.el when malyon loads. +;; Top-level side effects: sets malyon-stories-directory after malyon loads. +;; Runtime requires: user-constants. ;; Direct test load: yes. ;; ;; Configuration for game packages. ;; -;; - Malyon for playing interactive fiction and text adventures in Z-machine format -;; (stories directory: ~/sync/org/text.games/) -;; - 2048 number-tile puzzle game +;; - Malyon: interactive fiction / Z-machine player (stories under ~/sync/org/text.games/). +;; - 2048: number-tile puzzle. ;; -;; init.el autoloads `malyon' and `2048-game' to this module instead of -;; requiring it eagerly, so the first invocation of either command loads -;; games-config, which configures and then loads the package. +;; malyon and 2048-game autoload their own commands via package.el, so this +;; module owns neither command -- it only supplies malyon's stories directory. +;; init.el loads it via `with-eval-after-load 'malyon', so it loads on first +;; use rather than at startup. ;; ;;; Code: -;; ----------------------------------- Malyon ---------------------------------- -;; text based adventure player +(require 'user-constants) ;; org-dir -(use-package malyon - :defer t - :commands (malyon) - :config +(with-eval-after-load 'malyon (setq malyon-stories-directory (concat org-dir "text.games/"))) -;; ------------------------------------ 2048 ----------------------------------- -;; combine numbered tiles to create the elusive number 2048. -(use-package 2048-game - :defer t - :commands (2048-game)) - (provide 'games-config) ;;; games-config.el ends here. diff --git a/tests/test-init-defer-games.el b/tests/test-init-defer-games.el index 0b85a1ea7..f3ec94de8 100644 --- a/tests/test-init-defer-games.el +++ b/tests/test-init-defer-games.el @@ -1,10 +1,12 @@ ;;; test-init-defer-games.el --- games-config Phase 4 deferral -*- lexical-binding: t; -*- ;;; Commentary: -;; games-config is deferred (load-graph Phase 4): init.el autoloads `malyon' -;; and `2048-game' instead of requiring the module eagerly. These tests guard -;; that the game commands stay reachable with the module unloaded, and that -;; loading the module still applies the one setting it owns. +;; games-config is deferred (load-graph Phase 4): malyon and 2048-game autoload +;; their own commands via package.el, and init.el loads games-config (which only +;; supplies malyon's config) via `with-eval-after-load 'malyon'. These tests +;; guard the command availability and exercise the real autoload-invocation path +;; that M-x uses, which is where an earlier cut regressed ("Autoloading +;; games-config.el failed to define function malyon"). ;;; Code: @@ -13,25 +15,31 @@ (ert-deftest test-init-defer-games-commands-autoload-without-module () "Normal: the game commands resolve with games-config unloaded. -This is the safety net for the deferral -- dropping the eager require keeps -malyon and 2048-game reachable only because the packages autoload their own -commands, so assert that holds." +Dropping the eager require keeps malyon and 2048-game reachable only because the +packages autoload their own commands, so assert that holds." (package-initialize) (should-not (featurep 'games-config)) (should (commandp 'malyon)) (should (commandp '2048-game))) -(ert-deftest test-init-defer-games-config-applies-malyon-stories-dir () - "Normal: loading games-config still applies malyon's stories directory. -The module is the config owner; deferring it must not drop the one setting it -adds (`malyon-stories-directory'), which use-package applies when malyon loads." +(ert-deftest test-init-defer-games-malyon-loads-and-configures () + "Normal: resolving malyon's autoload yields a real command and applies config. +Reproduces the M-x malyon path via `autoload-do-load': malyon autoloads from its +own package, init.el's `with-eval-after-load 'malyon' loads games-config, and +games-config sets the stories directory. This is the regression guard for the +earlier cut that autoloaded malyon to games-config, where Emacs errored that the +load failed to define malyon." (package-initialize) (add-to-list 'load-path (expand-file-name "modules" default-directory)) (require 'user-constants) + (unless (and (fboundp 'malyon) (autoloadp (symbol-function 'malyon))) + (ert-skip "malyon package not available as an autoload")) (let ((org-dir "/tmp/games-defer-test/")) - (load "games-config" nil t) - (unless (require 'malyon nil t) - (ert-skip "malyon package not available")) + (with-eval-after-load 'malyon (require 'games-config)) ; the init.el wiring + (should-not (featurep 'games-config)) + (should (functionp (autoload-do-load (symbol-function 'malyon) 'malyon))) + (should (commandp 'malyon)) + (should (featurep 'games-config)) (should (equal malyon-stories-directory "/tmp/games-defer-test/text.games/")))) (provide 'test-init-defer-games) -- cgit v1.2.3