wrapper-manager-fds/modules: overhaul wrapper config

Now, there could be multiple wrappers within the configuration but it
should still result with one derivation unlike the original version.
This could be handy for making package overrides with multiple binaries
(for example, 7Z) while making the interface consistent. This turns out
to be way nicer than I thought which is a good thing.
This commit is contained in:
Gabriel Arazas 2024-07-08 21:12:31 +08:00
parent 393465ec47
commit 28dfaefc20
9 changed files with 198 additions and 182 deletions

View File

@ -11,10 +11,7 @@ let
modulesPath = builtins.toString ../wrapper-manager;
};
modules = [
({ lib, name, ... }: {
imports = [ ../wrapper-manager ];
config.executableName = lib.mkDefault name;
})
../wrapper-manager
] ++ cfg.sharedModules;
};
in
@ -34,18 +31,17 @@ in
[
{
config.build = {
variant = "package";
isBinary = true;
};
}
]
'';
description = ''
Extra modules to be added to all of the wrappers.
Extra modules to be added to all of the wrapper-manager configurations.
'';
};
wrappers = lib.mkOption {
packages = lib.mkOption {
type = lib.types.attrsOf wrapperManagerModule;
description = ''
A set of wrappers to be added into the environment configuration.

View File

@ -11,9 +11,9 @@ in
config = lib.mkMerge [
{ wrapper-manager.extraSpecialArgs.hmConfig = config; }
(lib.mkIf (cfg.wrappers != {}) {
(lib.mkIf (cfg.packages != {}) {
home.packages =
lib.mapAttrsToList (_: wrapper: wrapper.build.toplevel) cfg.wrappers;
lib.mapAttrsToList (_: wrapper: wrapper.build.toplevel) cfg.packages;
})
] ;
}

View File

@ -11,9 +11,9 @@ in
config = lib.mkMerge [
{ wrapper-manager.extraSpecialArgs.nixosConfig = config; }
(lib.mkIf (cfg.wrappers != {}) {
(lib.mkIf (cfg.packages != {}) {
environment.systemPackages =
lib.mapAttrsToList (_: wrapper: wrapper.build.toplevel) cfg.wrappers;
lib.mapAttrsToList (_: wrapper: wrapper.build.toplevel) cfg.packages;
})
];
}

View File

@ -1,120 +1,45 @@
{ config, lib, ... }:
let
flagType = with lib.types; listOf (coercedTo anything (x: builtins.toString x) str);
in
{
options = {
prependArgs = lib.mkOption {
type = flagType;
wrappers = lib.mkOption {
type = with lib.types; attrsOf (submoduleWith {
modules = [ ./shared/wrappers.nix ];
specialArgs.envConfig = config;
});
description = ''
A list of arguments to be prepended to the user-given argument for the
wrapper script.
'';
default = [ ];
example = lib.literalExpression ''
[
"--config" ./config.conf
]
'';
};
appendArgs = lib.mkOption {
type = flagType;
description = ''
A list of arguments to be appended to the user-given argument for the
wrapper script.
'';
default = [ ];
example = lib.literalExpression ''
[
"--name" "doggo"
"--location" "Your mom's home"
]
'';
};
arg0 = lib.mkOption {
type = lib.types.str;
description = ''
The first argument of the wrapper script. This option is used when the
{option}`build.variant` is `executable`.
'';
example = lib.literalExpression "lib.getExe' pkgs.neofetch \"neofetch\"";
};
package = lib.mkOption {
type = lib.types.package;
description = ''
The package to be wrapped. This is used only when the
{option}`build.variant` is set to `package.`
'';
example = lib.literalExpression "pkgs.hello";
};
env = lib.mkOption {
type = with lib.types; attrsOf str;
description = ''
A set of environment variables to be declared in the wrapper script.
A set of wrappers to be included in the resulting derivation from
wrapper-manager evaluation.
'';
default = { };
example = {
"FOO_TYPE" = "custom";
"FOO_LOG_STYLE" = "systemd";
};
};
unset = lib.mkOption {
type = with lib.types; listOf nonEmptyStr;
description = ''
A list of environment variables to be unset into the wrapper script.
example = lib.literalExpression ''
{
yt-dlp-audio = {
arg0 = lib.getExe' pkgs.yt-dlp "yt-dlp";
prependArgs = [
"--config-location" ./config/yt-dlp/audio.conf
];
};
}
'';
default = [ ];
example = [ "NO_COLOR" ];
};
executableName = lib.mkOption {
type = lib.types.nonEmptyStr;
basePackages = lib.mkOption {
type = with lib.types; listOf package;
description = ''
The name of the executable of the wrapper script.
A list of packages to be included in the wrapper package.
This option behaves differently depending on {option}`build.variant`.
- When the build variant option is `executable`, it sets the name of the
wrapper script.
- When the build variant option is `package`, it depends on the name on
one of the executables from the given package.
'';
default =
if config.build.variant == "executable" then
lib.last (lib.path.subpath.components (lib.removePrefix "/" config.arg0))
else
config.package.meta.mainProgram or config.package.pname;
example = "custom-name";
};
pathAdd = lib.mkOption {
type = with lib.types; listOf path;
description = ''
A list of paths to be added as part of the `PATH` environment variable.
::: {note}
If the list is not empty, this can override some of the binaries
included in this list which is typically intended to be used as a
wrapped package.
:::
'';
default = [ ];
example = lib.literalExpression ''
wrapperManagerLib.getBin (with pkgs; [
with pkgs; [
yt-dlp
gallery-dl
])
'';
};
preScript = lib.mkOption {
type = lib.types.lines;
description = ''
Script to run before the main executable.
'';
default = "";
example = lib.literalExpression ''
echo "HELLO WORLD!"
]
'';
};
};

View File

@ -1,24 +1,7 @@
{ config, lib, pkgs, wrapperManagerLib, ... }:
{ config, lib, pkgs, ... }:
{
options.build = {
variant = lib.mkOption {
type = lib.types.enum [ "executable" "package" ];
description = ''
Tells how should wrapper-manager wrap the executable. The toplevel
derivation resulting from the module environment will vary depending on
the value.
- With `executable`, the wrapper is a lone executable wrapper script in
`$OUT/bin` subdirectory in the output.
- With `package`, wrapper-manager creates a wrapped package with all of
the output contents intact.
'';
default = "executable";
example = "package";
};
isBinary = lib.mkOption {
type = lib.types.bool;
description = ''
@ -29,24 +12,12 @@
example = false;
};
makeWrapperArgs = lib.mkOption {
type = with lib.types; listOf str;
extraSetup = lib.mkOption {
type = lib.types.lines;
description = ''
A list of extra arguments to be passed to the `makeWrapperArgs` build
step of the evaluation.
Additional script for setting up the wrapper script derivation.
'';
example = [ "--inherit-argv0" ];
};
extraArgs = lib.mkOption {
type = with lib.types; attrsOf anything;
description = ''
A attrset of extra arguments to be passed to the
`wrapperManagerLib.mkWrapper` function. This will also be passed as
part of the derivation attribute into the resulting script from
{option}`preScript`.
'';
default = { };
default = "";
};
toplevel = lib.mkOption {
@ -59,32 +30,25 @@
config = {
build = {
makeWrapperArgs = [
"--argv0" (config.executableName or config.arg0)
]
++ (lib.mapAttrsToList (n: v: "--set ${n} ${v}") config.env)
++ (builtins.map (v: "--unset ${v}") config.unset)
++ (builtins.map (v: "--prefix 'PATH' ':' ${lib.escapeShellArg v}") config.pathAdd)
++ (builtins.map (v: "--add-flags ${v}") config.prependArgs)
++ (builtins.map (v: "--append-flags ${v}") config.appendArgs)
++ (lib.optionals (!config.build.isBinary && config.preScript != "") (
toplevel =
let
preScript =
pkgs.runCommand "wrapper-script-prescript-${config.executableName}" config.build.extraArgs config.preScript;
mkWrapBuild = wrappers:
lib.concatMapStrings (v: ''
makeWrapper "${v.arg0}" "${builtins.placeholder "out"}/bin/${v.executableName}" ${lib.concatStringsSep " " v.makeWrapperArgs}
'') wrappers;
in
[ "--run" preScript ]));
toplevel =
if config.build.variant == "executable" then
wrapperManagerLib.mkWrapper (config.build.extraArgs // {
inherit (config) arg0 executableName;
inherit (config.build) isBinary makeWrapperArgs;
})
else
wrapperManagerLib.mkWrappedPackage (config.build.extraArgs // {
inherit (config) arg0 package executableName;
inherit (config.build) isBinary makeWrapperArgs;
});
pkgs.symlinkJoin {
name = "wrapper-manager-fds-wrapped-package";
paths = config.basePackages;
nativeBuildInputs =
if config.build.isBinary
then [ pkgs.makeBinaryWrapper ]
else [ pkgs.makeWrapper ];
postBuild = ''
${config.build.extraSetup}
${mkWrapBuild (lib.attrValues config.wrappers)}
'';
};
};
};
}

View File

@ -0,0 +1,127 @@
{ name, lib, config, pkgs, envConfig, ... }:
let
flagType = with lib.types; listOf (coercedTo anything (x: builtins.toString x) str);
in
{
options = {
prependArgs = lib.mkOption {
type = flagType;
description = ''
A list of arguments to be prepended to the user-given argument for the
wrapper script.
'';
default = [ ];
example = lib.literalExpression ''
[
"--config" ./config.conf
]
'';
};
appendArgs = lib.mkOption {
type = flagType;
description = ''
A list of arguments to be appended to the user-given argument for the
wrapper script.
'';
default = [ ];
example = lib.literalExpression ''
[
"--name" "doggo"
"--location" "Your mom's home"
]
'';
};
arg0 = lib.mkOption {
type = lib.types.str;
description = ''
The first argument of the wrapper script. This option is used when the
{option}`build.variant` is `executable`.
'';
example = lib.literalExpression "lib.getExe' pkgs.neofetch \"neofetch\"";
};
executableName = lib.mkOption {
type = lib.types.nonEmptyStr;
description = "The name of the executable.";
default = name;
example = "custom-name";
};
env = lib.mkOption {
type = with lib.types; attrsOf str;
description = ''
A set of environment variables to be declared in the wrapper script.
'';
default = { };
example = {
"FOO_TYPE" = "custom";
"FOO_LOG_STYLE" = "systemd";
};
};
unset = lib.mkOption {
type = with lib.types; listOf nonEmptyStr;
description = ''
A list of environment variables to be unset into the wrapper script.
'';
default = [ ];
example = [ "NO_COLOR" ];
};
pathAdd = lib.mkOption {
type = with lib.types; listOf path;
description = ''
A list of paths to be added as part of the `PATH` environment variable.
'';
default = [ ];
example = lib.literalExpression ''
wrapperManagerLib.getBin (with pkgs; [
yt-dlp
gallery-dl
])
'';
};
preScript = lib.mkOption {
type = lib.types.lines;
description = ''
Script fragments to run before the main executable. This option is only
used when the wrapper script is not compiled into a binary (that is,
when {option}`build.isBinary` is set to `false`).
'';
default = "";
example = lib.literalExpression ''
echo "HELLO WORLD!"
'';
};
makeWrapperArgs = lib.mkOption {
type = with lib.types; listOf str;
description = ''
A list of extra arguments to be passed as part of makeWrapper.
'';
example = [ "--inherit-argv0" ];
readOnly = true;
};
};
config = {
makeWrapperArgs = [
"--argv0" config.arg0
]
++ (lib.mapAttrsToList (n: v: "--set ${n} ${v}") config.env)
++ (builtins.map (v: "--unset ${v}") config.unset)
++ (builtins.map (v: "--prefix 'PATH' ':' ${lib.escapeShellArg v}") config.pathAdd)
++ (builtins.map (v: "--add-flags ${v}") config.prependArgs)
++ (builtins.map (v: "--append-flags ${v}") config.appendArgs)
++ (lib.optionals (!envConfig.build.isBinary && config.preScript != "") (
let
preScript =
pkgs.runCommand "wrapper-script-prescript-${config.executableName}" { } config.preScript;
in
[ "--run" preScript ]));
};
}

View File

@ -10,7 +10,7 @@ lib.runTests {
specialArgs.yourMomName = "Joe Mama";
};
in
lib.isDerivation sampleConf.config.build.toplevel;
lib.isDerivation sampleConf;
expected = true;
};
@ -23,7 +23,7 @@ lib.runTests {
specialArgs.yourMomName = "Joe Mama";
};
in
lib.isDerivation sampleConf.config.build.toplevel;
lib.isDerivation sampleConf;
expected = true;
};
}

View File

@ -1,7 +1,9 @@
{ lib, pkgs, ... }:
{
arg0 = lib.getExe' pkgs.fastfetch "fastfetch";
appendArgs = [ "--logo" "Guix" ];
env.NO_COLOR = "1";
wrappers.fastfetch = {
arg0 = lib.getExe' pkgs.fastfetch "fastfetch";
appendArgs = [ "--logo" "Guix" ];
env.NO_COLOR = "1";
};
}

View File

@ -1,11 +1,13 @@
{ lib, pkgs, yourMomName, ... }:
{
arg0 = lib.getExe' pkgs.neofetch "neofetch";
executableName = yourMomName;
appendArgs = [
"--ascii_distro" "guix"
"--title_fqdn" "off"
"--os_arch" "off"
];
wrappers.neofetch = {
arg0 = lib.getExe' pkgs.neofetch "neofetch";
executableName = yourMomName;
appendArgs = [
"--ascii_distro" "guix"
"--title_fqdn" "off"
"--os_arch" "off"
];
};
}