From f5cf6c74f046647240a3a7070c4e6cf28d24e849 Mon Sep 17 00:00:00 2001 From: tv Date: Wed, 8 May 2024 17:03:57 +0200 Subject: pkgs: add overlay for shell scripts --- pkgs/shell/default.nix | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 pkgs/shell/default.nix (limited to 'pkgs/shell') diff --git a/pkgs/shell/default.nix b/pkgs/shell/default.nix new file mode 100644 index 0000000..119b23e --- /dev/null +++ b/pkgs/shell/default.nix @@ -0,0 +1,76 @@ +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 ./.)) -- cgit v1.2.3