{ pkgs, lib, ... }: rec { mkWrapper = { basePackage, prependFlags ? [], appendFlags ? [], pathAdd ? [], environment ? {}, extraPackages ? [] }: pkgs.symlinkJoin ({ name = "wrapper-${basePackage.name}"; paths = [ basePackage ] ++ extraPackages; nativeBuildInputs = [ pkgs.makeWrapper ]; postBuild = let envToWrapperArg = name: config: let optionStr = attr: lib.showOption ["env" name attr]; unsetArg = if !config.force then (lib.warn '' ${optionStr "value"} is null (indicating unsetting the variable), but ${optionStr "force"} is false. This option will have no effect '' []) else ["--unset" config.name]; setArg = let arg = if config.force then "--set" else "--set-default"; in [arg config.name config.value]; in if config.value == null then unsetArg else setArg; envArgs = lib.mapAttrsToList envToWrapperArg environment; # 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); in '' for file in $out/bin/*; do wrapProgram \ "$file" \ ${lib.escapeShellArgs allArgs} done ${ lib.concatMapStringsSep "\n" (p: if lib.hasAttr "man" p then "${pkgs.xorg.lndir}/bin/lndir -silent ${p.man} $out" else "#") ([basePackage] ++ extraPackages) } ''; passthru = (basePackage.passhtru or {}) // {unwrapped = basePackage;}; }); escapeTmpFileContents = contents: builtins.replaceStrings ["\n"] ["\\n"] contents; mkTmpFile = { type ? "f", target, mode ? "0700", user, group, contents }: "${type} \"${target}\" ${mode} ${user} ${group} - ${escapeTmpFileContents contents}"; mkRmTmpFile = { type ? "f", target }: "${if type == "d" then "R" else "r"} ${target}"; tmpFileType = lib.types.submodule ({ config, ... }: { options = { enable = lib.mkOption { type = lib.types.bool; default = true; }; type = lib.mkOption { type = lib.types.str; default = "f"; }; mode = lib.mkOption { type = lib.types.str; default = "0400"; }; user = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; }; group = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; }; executable = lib.mkOption { type = lib.types.bool; default = false; }; target = lib.mkOption { type = lib.types.str; }; source = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; }; text = lib.mkOption { type = lib.types.nullOr lib.types.lines; default = null; }; }; }); homeFileType = (lib.types.submodule ({ config, name, ... }: { options = { enable = lib.mkOption { type = lib.types.bool; default = true; }; type = lib.mkOption { type = lib.types.str; default = "f"; }; mode = lib.mkOption { type = lib.types.str; default = "0400"; }; user = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; }; group = lib.mkOption { type = lib.types.nullOr lib.types.str; default = null; }; executable = lib.mkOption { type = lib.types.bool; default = false; }; target = lib.mkOption { type = lib.types.str; }; source = lib.mkOption { type = lib.types.nullOr lib.types.path; default = null; }; text = lib.mkOption { type = lib.types.nullOr lib.types.lines; default = null; }; }; config = { target = lib.mkDefault name; source = lib.mkIf (config.text != null) (lib.mkDefault (pkgs.writeTextFile { inherit (config) text; executable = config.executable == true; name = storeFileName config.target; })); }; })); # Figures out a valid Nix store name for the given path. storeFileName = path: let # All characters that are considered safe. Note "-" is not # included to avoid "-" followed by digit being interpreted as a # version. safeChars = [ "+" "." "_" "?" "=" ] ++ lib.lowerChars ++ lib.upperChars ++ lib.stringToCharacters "0123456789"; empties = l: lib.genList (x: "") (lib.length l); unsafeInName = lib.stringToCharacters (lib.replaceStrings safeChars (empties safeChars) path); safeName = lib.replaceStrings unsafeInName (empties unsafeInName) path; in "home_" + safeName; }