emacs-overlay.url = "github:nix-community/emacs-overlay";
fromElisp.url = "github:talyz/fromElisp";
fromElisp.flake = false;
emacs-igc.url= "github:emacs-mirror/emacs/feature/igc";
emacs-igc.flake = false;Pass-in the input as a named argument to outputs, so that it is available in modules:
I am wiring up my Emacs distribution using the withPackages function
described in the Nixpkgs Manual here. Currently I am experimenting
retargeting user-emacs-directory to this repository, so I don’t need
to rebuild Emacs every time I want to adjust its configuration.
Now I am experimenting Psionikus’ optimized build method.
{
vlaci-emacs =
let
inherit (pkgs) lib;
pwd = builtins.getEnv "PWD";
initDirectory = "${pwd}/out/emacs.d";
dicts = with pkgs.hunspellDicts; [
# See jinx below
hu-hu
en-us-large
];
dictSearchPath = lib.makeSearchPath "share/hunspell" dicts;
overlaid = inputs.emacs-overlay.overlays.default pkgs pkgs;
emacsPkg = overlaid.emacs-git-pgtk.overrideAttrs (old: {
pname = "emacs-igc-pgtk";
src = inputs.emacs-igc;
stdenv = pkgs.llvmPackages.stdenv;
buildInputs = old.buildInputs ++ [ pkgs.mps ];
configureFlags = old.configureFlags ++ [
"--with-mps=yes"
];
preConfigure = ''
export CC=${pkgs.llvmPackages.clang}/bin/clang
export CXX=${pkgs.llvmPackages.clang}/bin/clang++
export AR=${pkgs.llvm}/bin/llvm-ar
export NM=${pkgs.llvm}/bin/llvm-nm
export LD=${pkgs.lld}/bin/ld.lld
export RANLIB=${pkgs.llvm}/bin/llvm-ranlib
'';
# Extra compiler flags (Clang-flavored)
NIX_CFLAGS_COMPILE = toString ([
"-O2"
"-march=znver2"
"-mtune=znver2"
"-flto=full"
"-fprofile-use=${../assets/emacs.profdata}"
] ++ old.NIX_CFLAGS_COMPILE or []);
});
emacsWithPackages =
inputs.emacs-overlay.lib.${pkgs.system}.emacsPackagesFor emacsPkg;
fromElisp = import inputs.fromElisp { inherit pkgs; };
parseSetup =
with builtins;
string:
let
setups = lib.pipe (fromElisp.fromElisp string) [
(filter (block: head block == "setup"))
(map tail)
];
collect =
keyword:
with builtins;
let
go =
{
data,
rest,
}@acc:
fields: {
data =
data
++ lib.pipe fields [
(map (
field: if isList field && length field > 0 && head field == keyword then tail field else null
))
(filter lib.isList)
];
rest =
(lib.pipe fields [
(filter isList)
concatLists
(filter isList)
])
++ rest;
};
recurse =
{ rest, ... }@acc:
if rest == [ ] then removeAttrs acc [ "rest" ] else recurse (go (acc // { rest = [ ]; }) rest);
in
blocks:
lib.pipe blocks [
(foldl' go {
data = [ ];
rest = [ ];
})
recurse
(attrs: attrs.data)
];
in
lib.pipe (collect ":package" setups) [
lib.concatLists
lib.unique
];
gatherPackages =
initDir:
let
files = builtins.filter (f: lib.hasSuffix ".el" f) (lib.filesystem.listFilesRecursive initDir);
fromElisp = import inputs.fromElisp { inherit pkgs; };
in
lib.flatten (
map (
f:
let
configText = builtins.readFile f;
in
parseSetup configText
) files
);
detectedPackages = gatherPackages ./emacs.d;
emacs =
(emacsWithPackages.overrideScope (
lib.composeManyExtensions [
(final: prev: {
mkPackage =
{
pname,
src,
files ? [ "*.el" ],
...
}@args:
let
files' =
let
list = lib.concatStringsSep " " (map (f: ''"${lib.escape [ ''"'' ] f}"'') files);
in
"(${list})";
version =
let
ver = src.lastModifiedDate or inputs.self.lastModifiedDate;
removeLeadingZeros =
s:
let
s' = lib.removePrefix "0" s;
in
if lib.hasPrefix "0" s' then removeLeadingZeros s' else s';
major = removeLeadingZeros (builtins.substring 0 8 ver);
minor = removeLeadingZeros (builtins.substring 8 6 ver);
in
args.version or "${major}.${minor}";
in
final.melpaBuild (
{
inherit version src;
commit =
src.rev or inputs.self.sourceInfo.rev or inputs.self.sourceInfo.dirtyRev
or "00000000000000000000000000000000";
recipe = pkgs.writeText "recipe" ''
(${pname}
:fetcher git
:url "nohost.nodomain"
:files ${files'})
'';
}
// removeAttrs args [ "files" ]
);
})
<<emacs-package-overrides>>
]
)).withPackages
(epkgs: (map (ename: epkgs.${ename}) detectedPackages) ++ [ epkgs.vlaci-emacs ]);
binaries = with pkgs; [
<<emacs-nixpkgs>>
];
in
assert lib.assertMsg (pwd != "") "Use --impure flag for building";
emacs.overrideAttrs (super: {
# instead of relyiong on `package.el` to wire-up autoloads, do it build-time
deps = super.deps.overrideAttrs (
dsuper:
let
genAutoloadsCommand = ''
echo "-- Generating autoloads..."
autoloads=$out/share/emacs/site-lisp/autoloads.el
for pkg in "''${requires[@]}"; do
autoload=("$pkg"/share/emacs/site-lisp/*/*/*-autoloads.el)
if [[ -e "$autoload" ]]; then
cat "$autoload" >> "$autoloads"
fi
done
echo "(load \"''$autoloads\")" >> "$siteStart"
# Byte-compiling improves start-up time only slightly, but costs nothing.
$emacs/bin/emacs --batch -f batch-byte-compile "$autoloads" "$siteStart"
$emacs/bin/emacs --batch \
--eval "(add-to-list 'native-comp-eln-load-path \"$out/share/emacs/native-lisp/\")" \
-f batch-native-compile "$autoloads" "$siteStart"
'';
in
{
buildCommand = ''
${dsuper.buildCommand}
${genAutoloadsCommand}
'';
}
);
buildCommand = ''
${super.buildCommand}
wrapProgram $out/bin/emacs \
--append-flags "--init-directory ${initDirectory}" \
--suffix PATH : ${
with lib;
pipe binaries [
makeBinPath
escapeShellArg
]
} \
--prefix DICPATH : ${lib.escapeShellArg dictSearchPath}
'';
});
}Lets add it to installed packages:
{ pkgs, ... }:
{
home.packages = [ pkgs.vlaci-emacs ];
}{
_.persist.allUsers.directories = [ ".cache/emacs" ];
}