self: super: let inherit (super) lib mylib; # Takes a path to a shellscript an builds it into a package. # # The shellscript may contain instructions for modifying the PATH variable: # #!buildShellBin path=PKGS resets the PATH variable to the specified packages # #!buildShellBin append-path=PKGS append the packages to the PATH variable # #!buildShellBin prepend-path=PKGS prepend the packages to the PATH variable # where # PKGS is a non-empty, colon-delimited list of package attribute names, e.g. coreutils:binutils:which buildShellBin = name: path: assert mylib.types.filename.check name; assert isShellScript path; self.callPackage ({ pkgs }: pkgs.runCommand "${name}" {} /* sh */ '' mkdir -p $out/bin touch $out/bin/${name} chmod +x $out/bin/${name} exec > $out/bin/${name} echo '#! ${pkgs.dash}/bin/dash' echo ${mylib.shell.escape (lib.concatStringsSep "\n" (pathMods pkgs path))} cat ${path} '') {}; # Determine if a line is an instruction to modify the PATH variable. readPathMod = pkgs: path: line: string: let match = builtins.match "^#! *buildShellBin ((prepend-|append-)?path)=(.*)" string; in lib.optionalAttrs (match != null) { inherit path line; method = builtins.elemAt match 0; packages = map (name: pkgs.${name}) (lib.splitString ":" (builtins.elemAt match 2)); }; # Generate shell assignment to modify the PATH variable. applyPathMod = { method, packages, ... }: { path = /* sh */ "PATH=${lib.makeBinPath packages}"; append-path = /* sh */ "PATH=\${PATH+$PATH:}${lib.makeBinPath packages}"; prepend-path = /* sh */ "PATH=${lib.makeBinPath packages}\${PATH+:$PATH}"; }.${method}; applyPathMods = mods: assert checkPathMods mods; map applyPathMod mods; # Check if a list of list of PATH modifications for sanity. checkPathMods = mods: let index = lib.lists.findFirstIndex (mod: mod.method == "path") 0 mods; mod = lib.elemAt mods index; in if index > 0 then builtins.trace "warning: resetting PATH in ${toString mod.path}:${toString mod.line}" true else true; pathMods = pkgs: path: applyPathMods (builtins.filter (x: x != {}) (lib.imap1 (readPathMod pkgs path) (lib.splitString "\n" (builtins.readFile path)))); isShellScript = path: mylib.test "#! */bin/sh" (builtins.head (lib.splitString "\n" (builtins.readFile path))); in lib.mapAttrs (name: _: buildShellBin name (./. + "/${name}")) (lib.filterAttrs (name: _: name != "default.nix") (builtins.readDir ./.))