Use --init-directory in Emacs 29+ (#191)

* Use `--init-directory` in Emacs 29+

This will allow a few advantages:
- We can load `early-init.el` file properly, fixing Flash Of Unstyled
Contents (FOUC) issues and improving the performance slightly
- Eventually we can drop `default.el` file loading in Home-Manager
module. Not done here since this would complicate the code without
necessity, however this can be done once Emacs 29+ is default

* Fix build in Emacs <29

* Add #checks.<arch>.init-example-el-emacsGit

* Add .#checks.x86_64-linux.init-example-el-emacsGit to CI

* Fix review issues

* Move config files to share/emacs.d

* Separate tests in two steps

* Add TODOS

* Simplify emacs-dir generation
This commit is contained in:
Thiago Kenji Okada
2022-07-10 18:46:49 +01:00
committed by GitHub
parent a59295c11e
commit 9111d480a8
4 changed files with 101 additions and 26 deletions

View File

@ -6,8 +6,8 @@ on:
- master - master
- develop - develop
jobs: jobs:
check: check-emacs:
name: Flake Check (x86_64 only) name: Flake Check emacs (x86_64 only)
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -34,9 +34,43 @@ jobs:
nix-store --import < nix-store.dump || true nix-store --import < nix-store.dump || true
rm nix-store.dump rm nix-store.dump
fi fi
- run: | - name: Run checks in emacs
run: |
nix build .#checks.x86_64-linux.init-example-el nix build .#checks.x86_64-linux.init-example-el
- name: Export /nix/store contents - name: Export /nix/store contents
if: ${{ !steps.cache-nix-store.outputs.cache-hit }} if: ${{ !steps.cache-nix-store.outputs.cache-hit }}
run: | run: |
nix-store --export $(nix-store -qR /nix/store/*-doom-emacs) > nix-store.dump nix-store --export $(nix-store -qR /nix/store/*-doom-emacs) > nix-store.dump
check-emacsGit:
name: Flake Check emacsGit (x86_64 only)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
# Nix Flakes doesn't work on shallow clones
fetch-depth: 0
- uses: cachix/install-nix-action@v17
with:
name: nix-community
- name: Retrieve /nix/store archive
uses: actions/cache@v3
id: cache-nix-store-emacsGit
with:
path: nix-store.dump
key: nix-store-emacsGit-${{ hashFiles('flake.*') }}
restore-keys: |
nix-store-emacsGit-
- name: Import /nix/store contents
if: ${{ steps.cache-nix-store-emacsGit.outputs.cache-hit }}
run: |
if [[ -f nix-store.dump ]]; then
nix-store --import < nix-store.dump || true
rm nix-store.dump
fi
- name: Run checks in emacsGit
run: |
nix build .#checks.x86_64-linux.init-example-el-emacsGit
- name: Export /nix/store contents
if: ${{ !steps.cache-nix-store-emacsGit.outputs.cache-hit }}
run: |
nix-store --export $(nix-store -qR /nix/store/*-doom-emacs) > nix-store.dump

View File

@ -1,5 +1,5 @@
{ # The files would be going to ~/.config/doom (~/.doom.d) { # The files would be going to ~/.config/doom (~/.doom.d)
doomPrivateDir doomPrivateDir
/* Extra packages to install /* Extra packages to install
Useful for non-emacs packages containing emacs bindings (e.g. Useful for non-emacs packages containing emacs bindings (e.g.
@ -8,8 +8,7 @@ doomPrivateDir
Example: Example:
extraPackages = epkgs: [ pkgs.mu ]; extraPackages = epkgs: [ pkgs.mu ];
*/ */
, extraPackages ? epkgs: , extraPackages ? epkgs: [ ]
[ ]
/* Extra configuration to source during initialization /* Extra configuration to source during initialization
Use this to refer other nix derivations. Use this to refer other nix derivations.
@ -36,8 +35,7 @@ doomPrivateDir
}); });
}; };
*/ */
, emacsPackagesOverlay ? self: super: , emacsPackagesOverlay ? self: super: { }
{ }
/* Use bundled revision of github.com/nix-community/emacs-overlay /* Use bundled revision of github.com/nix-community/emacs-overlay
as `emacsPackages`. as `emacsPackages`.
*/ */
@ -54,7 +52,8 @@ doomPrivateDir
"emacs-overlay" = fetchFromGitHub { owner = /* ...*\/; }; "emacs-overlay" = fetchFromGitHub { owner = /* ...*\/; };
}; };
*/ */
, dependencyOverrides ? { }, lib, pkgs, stdenv, buildEnv, makeWrapper , dependencyOverrides ? { }
, lib, pkgs, stdenv, buildEnv, makeWrapper
, runCommand, fetchFromGitHub, substituteAll, writeShellScript , runCommand, fetchFromGitHub, substituteAll, writeShellScript
, writeShellScriptBin, writeTextDir }: , writeShellScriptBin, writeTextDir }:
@ -63,6 +62,7 @@ assert (lib.assertMsg ((builtins.isPath doomPrivateDir)
"doomPrivateDir must be either a path, a derivation or a stringified store path"); "doomPrivateDir must be either a path, a derivation or a stringified store path");
let let
isEmacs29 = lib.versionAtLeast emacsPackages.emacs.version "29";
flake = flake =
(import (import
(let lock = with builtins; fromJSON (readFile ./flake.lock); in (let lock = with builtins; fromJSON (readFile ./flake.lock); in
@ -193,7 +193,7 @@ let
}; };
# Stage 4: `extraConfig` is merged into private configuration # Stage 4: `extraConfig` is merged into private configuration
doomDir = pkgs.runCommand "doom-private" { doomDir = runCommand "doom-private" {
inherit extraConfig; inherit extraConfig;
passAsFile = [ "extraConfig" ]; passAsFile = [ "extraConfig" ];
} '' } ''
@ -202,23 +202,38 @@ let
chmod u+w $out/config.el chmod u+w $out/config.el
cat $extraConfigPath > $out/config.extra.el cat $extraConfigPath > $out/config.extra.el
cat > $out/config.el << EOF cat > $out/config.el << EOF
(load "${builtins.toString doomPrivateDir}/config.el") (load "${doomPrivateDir}/config.el")
(load "$out/config.extra.el") (load "$out/config.extra.el")
EOF EOF
''; '';
# Stage 5: catch-all wrapper capable to run doom-emacs even # Stage 5: catch-all wrapper capable to run doom-emacs even
# without installing ~/.emacs.d # without installing ~/.emacs.d
# TODO: remove once Emacs 29+ is released and commonly available
emacs = let emacs = let
load-config-from-site = writeTextDir "share/emacs/site-lisp/default.el" '' load-config-from-site = writeTextDir "share/emacs/site-lisp/default.el" ''
(message "doom-emacs is not placed in `doom-private-dir', (message "doom-emacs is not placed in `doom-private-dir',
loading from `site-lisp'") loading from `site-lisp'")
(when (> emacs-major-version 26) ${lib.optionalString (!isEmacs29) ''
(load "${doom-emacs}/early-init.el")) (load "${doom-emacs}/early-init.el")
(load "${doom-emacs}/core/core-start.el") (load "${doom-emacs}/core/core-start.el")
''}
''; '';
in (emacsPackages.emacsWithPackages (epkgs: [ load-config-from-site ])); in (emacsPackages.emacsWithPackages (epkgs: [ load-config-from-site ]));
# create a `emacs.d` dir to be loaded using `--init-directory` flag from Emacs 29+
# this will allow proper usage of `early-init.el`, fixing FOUC issues and improving
# startup performance
emacs-dir = runCommand "emacs-dir" { } ''
mkdir -p $out
cat > $out/early-init.el << EOF
(load "${doom-emacs}/early-init.el")
EOF
cat > $out/init.el << EOF
(load "${doom-emacs}/core/core-start.el")
EOF
'';
build-summary = writeShellScript "build-summary" '' build-summary = writeShellScript "build-summary" ''
printf "\n${fmt.green}Successfully built nix-doom-emacs!${fmt.reset}\n" printf "\n${fmt.green}Successfully built nix-doom-emacs!${fmt.reset}\n"
printf "${fmt.bold} ==> doom-emacs is installed to ${doom-emacs}${fmt.reset}\n" printf "${fmt.bold} ==> doom-emacs is installed to ${doom-emacs}${fmt.reset}\n"
@ -227,13 +242,23 @@ let
''; '';
in emacs.overrideAttrs (esuper: in emacs.overrideAttrs (esuper:
let let
# `--init-directory` is supported by Emacs 29+ only
initDirArgs = lib.optionalString isEmacs29 ''
if [[ $(basename $1) == emacs ]] || [[ $(basename $1) == emacs-* ]]; then
wrapArgs+=(--add-flags '--init-directory ${emacs-dir}')
fi
'';
cmd = '' cmd = ''
wrapEmacs() { wrapEmacs() {
wrapProgram $1 \ local -a wrapArgs=(
--set DOOMDIR ${doomDir} \ --set DOOMDIR ${doomDir}
--set NIX_DOOM_EMACS_BINARY $1 \ --set NIX_DOOM_EMACS_BINARY $1
--set __DEBUG_doom_emacs_DIR ${doom-emacs} \ --set __DEBUG_doom_emacs_DIR ${doom-emacs}
--set __DEBUG_doomLocal_DIR ${doomLocal} --set __DEBUG_doomLocal_DIR ${doomLocal}
)
${initDirArgs}
wrapProgram $1 "''${wrapArgs[@]}"
} }
for prog in $out/bin/*; do for prog in $out/bin/*; do

View File

@ -87,7 +87,7 @@
flake-compat.flake = false; flake-compat.flake = false;
}; };
outputs = { self, nixpkgs, flake-utils, ... }@inputs: outputs = { self, nixpkgs, flake-utils, emacs-overlay, ... }@inputs:
let inherit (flake-utils.lib) eachDefaultSystem eachSystem; let inherit (flake-utils.lib) eachDefaultSystem eachSystem;
in eachDefaultSystem (system: in eachDefaultSystem (system:
let pkgs = import nixpkgs { inherit system; }; let pkgs = import nixpkgs { inherit system; };
@ -100,12 +100,27 @@
pkgs.callPackage self pkgs.callPackage self
(args // { dependencyOverrides = (inputs // dependencyOverrides); }); (args // { dependencyOverrides = (inputs // dependencyOverrides); });
}) // eachSystem [ "x86_64-linux" "aarch64-darwin" ] (system: { }) // eachSystem [ "x86_64-linux" "aarch64-darwin" ] (system: {
checks = { checks =
init-example-el = self.outputs.package.${system} { let
doomPrivateDir = ./test/doom.d; pkgs = import nixpkgs {
dependencyOverrides = inputs; inherit system;
}; # we are not using emacs-overlay's flake.nix here,
}; # to avoid unnecessary inputs to be added to flake.lock;
# this means we need to import the overlay in a hack-ish way
overlays = [ (import emacs-overlay) ];
};
in
{
init-example-el = self.outputs.package.${system} {
doomPrivateDir = ./test/doom.d;
dependencyOverrides = inputs;
};
init-example-el-emacsGit = self.outputs.package.${system} {
doomPrivateDir = ./test/doom.d;
dependencyOverrides = inputs;
emacsPackages = with pkgs; emacsPackagesFor emacsGit;
};
};
}) // { }) // {
hmModule = import ./modules/home-manager.nix inputs; hmModule = import ./modules/home-manager.nix inputs;
}; };

View File

@ -91,6 +91,7 @@ in
in in
mkMerge ([ mkMerge ([
{ {
# TODO: remove once Emacs 29+ is released and commonly available
home.file.".emacs.d/init.el".text = '' home.file.".emacs.d/init.el".text = ''
(load "default.el") (load "default.el")
''; '';