Skip to content

Commit

Permalink
Added wrappers.<name>.programs attrset
Browse files Browse the repository at this point in the history
  • Loading branch information
nrabulinski committed Feb 9, 2024
1 parent b20240e commit 01b80d8
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 71 deletions.
5 changes: 5 additions & 0 deletions doc/content/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ https://github.com/viperML/wrapper-manager/issues

## Changelog

- TBA
- Added `programs` attrset which allows to wrap specific programs
- Added `wrapByDefault` which can be set to `false` to disable the previous behavior
- Deprecated `renames` instead of setting `programs.<name>.target`

- 2023-11-13
- Added `prependFlags`, which maps to `--add-flags`
- Added `appendFlags`, which maps to `--append-flags`
Expand Down
216 changes: 149 additions & 67 deletions modules/base.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,8 @@
types
;

wrapperOpts = {config, ...}: {
imports = [
(lib.mkAliasOptionModuleMD ["flags"] ["prependFlags"])
];

commonOpts = {
options = {
basePackage = mkOption {
type = with types; package;
description = lib.mdDoc ''
Program to be wrapped.
'';
example = lib.literalExpression "pkgs.nix";
};

extraPackages = mkOption {
type = with types; listOf package;
description = lib.mdDoc ''
Extra packages to also wrap.
'';
example = lib.literalExpression "[ pkgs.git-extras pkgs.delta ]";
default = [];
};

env = mkOption {
type = with types; attrsOf (submodule ./env-type.nix);
description = lib.mdDoc ''
Expand Down Expand Up @@ -90,6 +69,65 @@
default = "";
example = "--argv0 foo --set BAR value";
};
};
};

wrapperOpts = {
config,
name,
...
}: {
imports = [
commonOpts
(lib.mkAliasOptionModuleMD ["flags"] ["prependFlags"])
];

options = {
basePackage = mkOption {
type = with types; package;
description = lib.mdDoc ''
Program to be wrapped.
'';
example = lib.literalExpression "pkgs.nix";
};

extraPackages = mkOption {
type = with types; listOf package;
description = lib.mdDoc ''
Extra packages to also wrap.
'';
example = lib.literalExpression "[ pkgs.git-extras pkgs.delta ]";
default = [];
};

wrapByDefault = mkOption {
type = with types; bool;
description = lib.mdDoc ''
Whether to wrap all programs under bin/ by default.
'';
example = false;
default = true;
};

programs = mkOption {
type = with types;
attrsOf (submoduleWith {
shorthandOnlyDefinesConfig = true;
modules = [commonOpts ./program-type.nix];
specialArgs = {
defaults = config;
};
});
description = lib.mdDoc ''
Programs to wrap.
'';
example = {
fish = {
prependFlags = ["-C" "echo Hello, fish"];
};
};
default = {};
};

wrapped = mkOption {
type = with types; package;
Expand All @@ -113,6 +151,14 @@
};

config = {
programs = let
renamesOpt = lib.showOption ["wrappers" name "renames"];
suggestedOpt = lib.showOption ["wrappers" name "programs" "<name>" "target"];
in
lib.warnIf
(config.renames != {})
"${renamesOpt} is deprecated. Set ${suggestedOpt} instead"
(lib.mapAttrs (_: target: {inherit target;}) config.renames);
wrapped = let
envToWrapperArg = name: config: let
optionStr = attr: lib.showOption ["env" name attr];
Expand All @@ -133,27 +179,87 @@
if config.value == null
then unsetArg
else setArg;
wrapProgramStr = {
name,
target,
env,
prependFlags,
appendFlags,
pathAdd,
extraWrapperFlags,
...
}: let
envArgs = lib.mapAttrsToList envToWrapperArg env;
# Yes, the arguments are escaped later, yes, this is intended to "double escape",
# so that they are escaped for wrapProgram and for the final binary too.
prependFlagArgs = map (args: ["--add-flags" (lib.escapeShellArg args)]) prependFlags;
appendFlagArgs = map (args: ["--append-flags" (lib.escapeShellArg args)]) appendFlags;
pathArgs = map (p: ["--prefix" "PATH" ":" "${p}/bin"]) pathAdd;
allArgs = lib.flatten (envArgs ++ prependFlagArgs ++ appendFlagArgs ++ pathArgs);
renameStr = lib.optionalString (name != target) ''
mv -vf ${name} ${lib.escapeShellArg target}
'';
in ''
echo "Wrapping ${name}"
wrapProgram \
"$out/bin/${name}" \
${lib.escapeShellArgs allArgs} \
${extraWrapperFlags}
${renameStr}
exe="${name}"
newexe="${target}"
# Fix .desktop files
# This list of fixes might not be exhaustive
for file in $out/share/applications/*; do
echo "Fixing file=$file for exe=$exe"
set -x
trap "set +x" ERR
sed -i "s#/nix/store/.*/bin/$exe #$out/bin/$newexe #" "$file"
sed -i -E "s#Exec=$exe([[:space:]]*)#Exec=$out/bin/$newexe\1#g" "$file"
sed -i -E "s#TryExec=$exe([[:space:]]*)#TryExec=$out/bin/$newexe\1#g" "$file"
set +x
done
'';
defaultWrappers = let
moveOutPrograms =
lib.optionalString
(config.programs != {})
''
echo Moving explicitly wrapped programs
mv -vf ${lib.escapeShellArgs (lib.mapAttrsToList (_: p: p.name) config.programs)} $wrapped_temp_dir
'';
moveBackPrograms =
lib.optionalString
(config.programs != {})
''
echo Restoring explicitly wrapped programs
mv -vf $wrapped_temp_dir/* ./
'';
in
lib.optionalString config.wrapByDefault ''
wrapped_temp_dir=$(mktemp -d)
${moveOutPrograms}
for file in *; do
${wrapProgramStr (config
// {
name = "$file";
target = "$file";
})}
done
${moveBackPrograms}
'';
explicitWrappers =
lib.concatMapStringsSep
"\n"
wrapProgramStr
(lib.attrValues config.programs);
result =
pkgs.symlinkJoin ({
paths = [config.basePackage] ++ config.extraPackages;
nativeBuildInputs = [pkgs.makeWrapper];
postBuild = let
envArgs = lib.mapAttrsToList envToWrapperArg config.env;
# Yes, the arguments are escaped later, yes, this is intended to "double escape",
# so that they are escaped for wrapProgram and for the final binary too.
prependFlagArgs = map (args: ["--add-flags" (lib.escapeShellArg args)]) config.prependFlags;
appendFlagArgs = map (args: ["--append-flags" (lib.escapeShellArg args)]) config.appendFlags;
pathArgs = map (p: ["--prefix" "PATH" ":" "${p}/bin"]) config.pathAdd;
allArgs = lib.flatten (envArgs ++ prependFlagArgs ++ appendFlagArgs ++ pathArgs);
in ''
for file in $out/bin/*; do
echo "Wrapping $file"
wrapProgram \
$file \
${lib.escapeShellArgs allArgs} \
${config.extraWrapperFlags}
done
postBuild = ''
# Some derivations have nested symlinks here
if [[ -d $out/share/applications && ! -w $out/share/applications ]]; then
echo "Detected nested symlink, fixing"
Expand All @@ -164,34 +270,10 @@
cp -v $temp/* $out/share/applications
fi
cd $out/bin
for exe in *; do
if false; then
exit 2
${lib.concatStringsSep "\n" (lib.mapAttrsToList (name: value: ''
elif [[ $exe == ${lib.escapeShellArg name} ]]; then
newexe=${lib.escapeShellArg value}
mv -vf $exe $newexe
'')
config.renames)}
else
newexe=$exe
fi
# Fix .desktop files
# This list of fixes might not be exhaustive
for file in $out/share/applications/*; do
echo "Fixing file=$file for exe=$exe"
set -x
trap "set +x" ERR
sed -i "s#/nix/store/.*/bin/$exe #$out/bin/$newexe #" "$file"
sed -i -E "s#Exec=$exe([[:space:]]*)#Exec=$out/bin/$newexe\1#g" "$file"
sed -i -E "s#TryExec=$exe([[:space:]]*)#TryExec=$out/bin/$newexe\1#g" "$file"
set +x
done
done
pushd $out/bin
${defaultWrappers}
${explicitWrappers}
popd
# I don't know of a better way to create a multe-output derivation for symlinkJoin
# So if the packages have man, just link them into $out
Expand Down
39 changes: 39 additions & 0 deletions modules/program-type.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
config,
lib,
name,
defaults,
...
}: let
inherit (lib) mkOption types mdDoc literalMD;
in {
options = {
name = mkOption {
type = types.str;
description = mdDoc ''
Name of the program.
'';
default = name;
};

target = mkOption {
type = types.str;
description = mdDoc ''
The final name of the program after wrapping.
'';
default = config.name;
defaultText = literalMD "value of name";
};
};

config = {
inherit
(defaults)
env
prependFlags
appendFlags
pathAdd
extraWrapperFlags
;
};
}
8 changes: 4 additions & 4 deletions tests/test-module.nix
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
env.FOO.value = "foo";
env.BAR.value = "bar";
basePackage = pkgs.hello;
flags = [
appendFlags = [
"-g"
some-special-arg
];
Expand Down Expand Up @@ -45,14 +45,14 @@

wrappers.neovim = {
basePackage = pkgs.neovim;
renames = {
"nvim" = "nvim2";
programs.nvim = {
target = "nvim2";
};
};

wrappers.discord = {
basePackage = pkgs.discord;
flags = [
programs.discord.prependFlags = [
"--disable-gpu"
];
};
Expand Down

0 comments on commit 01b80d8

Please sign in to comment.