From 28dfaefc20c8d6ddc8d04070bf45144fe0ad67ce Mon Sep 17 00:00:00 2001 From: Gabriel Arazas Date: Mon, 8 Jul 2024 21:12:31 +0800 Subject: [PATCH] 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. --- modules/env/common.nix | 10 +- modules/env/home-manager/default.nix | 4 +- modules/env/nixos/default.nix | 4 +- modules/wrapper-manager/base.nix | 127 ++++---------------- modules/wrapper-manager/build.nix | 80 ++++-------- modules/wrapper-manager/shared/wrappers.nix | 127 ++++++++++++++++++++ tests/lib/env/default.nix | 4 +- tests/lib/env/wrapper-fastfetch.nix | 8 +- tests/lib/env/wrapper-neofetch.nix | 16 +-- 9 files changed, 198 insertions(+), 182 deletions(-) create mode 100644 modules/wrapper-manager/shared/wrappers.nix diff --git a/modules/env/common.nix b/modules/env/common.nix index 2f65de2..af0e05b 100644 --- a/modules/env/common.nix +++ b/modules/env/common.nix @@ -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. diff --git a/modules/env/home-manager/default.nix b/modules/env/home-manager/default.nix index 7f7b877..b79f176 100644 --- a/modules/env/home-manager/default.nix +++ b/modules/env/home-manager/default.nix @@ -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; }) ] ; } diff --git a/modules/env/nixos/default.nix b/modules/env/nixos/default.nix index a020ed0..ae45715 100644 --- a/modules/env/nixos/default.nix +++ b/modules/env/nixos/default.nix @@ -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; }) ]; } diff --git a/modules/wrapper-manager/base.nix b/modules/wrapper-manager/base.nix index ea0c5ea..1d8e6b3 100644 --- a/modules/wrapper-manager/base.nix +++ b/modules/wrapper-manager/base.nix @@ -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!" + ] ''; }; }; diff --git a/modules/wrapper-manager/build.nix b/modules/wrapper-manager/build.nix index 68676a1..6ac9490 100644 --- a/modules/wrapper-manager/build.nix +++ b/modules/wrapper-manager/build.nix @@ -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)} + ''; + }; }; }; } diff --git a/modules/wrapper-manager/shared/wrappers.nix b/modules/wrapper-manager/shared/wrappers.nix new file mode 100644 index 0000000..a11625c --- /dev/null +++ b/modules/wrapper-manager/shared/wrappers.nix @@ -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 ])); + }; +} diff --git a/tests/lib/env/default.nix b/tests/lib/env/default.nix index b2da63d..bb5eba8 100644 --- a/tests/lib/env/default.nix +++ b/tests/lib/env/default.nix @@ -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; }; } diff --git a/tests/lib/env/wrapper-fastfetch.nix b/tests/lib/env/wrapper-fastfetch.nix index c7af5ab..d967c93 100644 --- a/tests/lib/env/wrapper-fastfetch.nix +++ b/tests/lib/env/wrapper-fastfetch.nix @@ -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"; + }; } diff --git a/tests/lib/env/wrapper-neofetch.nix b/tests/lib/env/wrapper-neofetch.nix index 5092d04..9986e41 100644 --- a/tests/lib/env/wrapper-neofetch.nix +++ b/tests/lib/env/wrapper-neofetch.nix @@ -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" + ]; + }; }