1
0
Fork 0
mirror of https://github.com/nix-community/home-manager.git synced 2025-11-08 19:46:05 +01:00

fish: keep all fish-completions packages around

...even empty ones.

For packages in `home.packages` with no Fish completions, we build an
empty directory that is not included in our runtime closure (because we
build a collection of symlinks to their contents: if there are no
contents, there is nothing to symlink). This means these empty
completion packages get garbage-collected by Nix. They are not that
expensive to rebuild but there can be enough of them it adds up, and any
change to any package in `home.packages` is enough to trigger a rebuild.

Fix this by forcing a dependency on all of them. Since we already
depended on them anyway if they were non-empty, this only adds
dependencies on empty directories (so should not significantly affect
storage space).

Fixes #6157.
This commit is contained in:
Marien Zwart 2025-03-02 21:35:26 +11:00 committed by Robert Helgesson
parent 361ab4484e
commit f045bd46b7

View file

@ -434,92 +434,103 @@ in
lib.mkMerge [ lib.mkMerge [
{ home.packages = [ cfg.package ]; } { home.packages = [ cfg.package ]; }
(mkIf cfg.generateCompletions { (mkIf cfg.generateCompletions (
# Support completion for `man` by building a cache for `apropos`. let
programs.man.generateCaches = lib.mkDefault true; generateCompletions =
let
xdg.dataFile."fish/home-manager_generated_completions".source = getName =
let attrs: attrs.name or "${attrs.pname or "«pname-missing»"}-${attrs.version or "«version-missing»"}";
# Paths later in the list will overwrite those already linked in
destructiveSymlinkJoin = package:
args_@{ pkgs.runCommand "${getName package}-fish-completions"
name, {
paths, srcs =
preferLocalBuild ? true, [ package ]
allowSubstitutes ? false, ++ lib.filter (p: p != null) (
postBuild ? "", builtins.map (outName: package.${outName} or null) config.home.extraOutputsToInstall
... );
}: nativeBuildInputs = [ pkgs.python3 ];
let buildInputs = [ cfg.package ];
args = preferLocalBuild = true;
removeAttrs args_ [ }
"name" ''
"postBuild"
]
// {
# pass the defaults
inherit preferLocalBuild allowSubstitutes;
};
in
pkgs.runCommand name args ''
mkdir -p $out mkdir -p $out
for i in $paths; do for src in $srcs; do
if [ -z "$(find $i -prune -empty)" ]; then if [ -d $src/share/man ]; then
cp -srf $i/* $out find -L $src/share/man -type f \
-exec python ${cfg.package}/share/fish/tools/create_manpage_completions.py --directory $out {} + \
> /dev/null
fi fi
done done
${postBuild}
''; '';
generateCompletions = allCompletions =
let let
getName = cmp = (a: b: (a.meta.priority or 0) > (b.meta.priority or 0));
attrs: attrs.name or "${attrs.pname or "«pname-missing»"}-${attrs.version or "«version-missing»"}"; in
in map generateCompletions (lib.sort cmp config.home.packages);
package: in
pkgs.runCommand "${getName package}-fish-completions" {
{ # Support completion for `man` by building a cache for `apropos`.
srcs = programs.man.generateCaches = lib.mkDefault true;
[ package ]
++ lib.filter (p: p != null) ( xdg.dataFile."fish/home-manager_generated_completions".source =
builtins.map (outName: package.${outName} or null) config.home.extraOutputsToInstall let
); # Paths later in the list will overwrite those already linked
nativeBuildInputs = [ pkgs.python3 ]; destructiveSymlinkJoin =
buildInputs = [ cfg.package ]; args_@{
preferLocalBuild = true; name,
} paths,
'' preferLocalBuild ? true,
allowSubstitutes ? false,
postBuild ? "",
...
}:
let
args =
removeAttrs args_ [
"name"
"postBuild"
]
// {
# pass the defaults
inherit preferLocalBuild allowSubstitutes;
};
in
pkgs.runCommand name args ''
mkdir -p $out mkdir -p $out
for src in $srcs; do for i in $paths; do
if [ -d $src/share/man ]; then if [ -z "$(find $i -prune -empty)" ]; then
find -L $src/share/man -type f \ cp -srf $i/* $out
-exec python ${cfg.package}/share/fish/tools/create_manpage_completions.py --directory $out {} + \
> /dev/null
fi fi
done done
${postBuild}
''; '';
in
destructiveSymlinkJoin {
name = "${config.home.username}-fish-completions";
paths =
let
cmp = (a: b: (a.meta.priority or 0) > (b.meta.priority or 0));
in
map generateCompletions (lib.sort cmp config.home.packages);
};
programs.fish.interactiveShellInit = '' in
# add completions generated by Home Manager to $fish_complete_path destructiveSymlinkJoin {
begin name = "${config.home.username}-fish-completions";
set -l joined (string join " " $fish_complete_path) paths = allCompletions;
set -l prev_joined (string replace --regex "[^\s]*generated_completions.*" "" $joined) };
set -l post_joined (string replace $prev_joined "" $joined)
set -l prev (string split " " (string trim $prev_joined)) # For packages with no Fish completions, generateCompletions will build an empty directory,
set -l post (string split " " (string trim $post_joined)) # which means they will not be in our runtime closure. Force a dependency so these do not get
set fish_complete_path $prev "${config.xdg.dataHome}/fish/home-manager_generated_completions" $post # constantly rebuilt.
end home.extraDependencies = allCompletions;
'';
}) programs.fish.interactiveShellInit = ''
# add completions generated by Home Manager to $fish_complete_path
begin
set -l joined (string join " " $fish_complete_path)
set -l prev_joined (string replace --regex "[^\s]*generated_completions.*" "" $joined)
set -l post_joined (string replace $prev_joined "" $joined)
set -l prev (string split " " (string trim $prev_joined))
set -l post (string split " " (string trim $post_joined))
set fish_complete_path $prev "${config.xdg.dataHome}/fish/home-manager_generated_completions" $post
end
'';
}
))
{ {
xdg.configFile."fish/config.fish".source = fishIndent "config.fish" '' xdg.configFile."fish/config.fish".source = fishIndent "config.fish" ''