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" + ]; + }; }