about summary refs log tree commit diff
path: root/nix/writeExecline
diff options
context:
space:
mode:
Diffstat (limited to 'nix/writeExecline')
-rw-r--r--nix/writeExecline/OWNERS3
-rw-r--r--nix/writeExecline/default.nix67
2 files changed, 70 insertions, 0 deletions
diff --git a/nix/writeExecline/OWNERS b/nix/writeExecline/OWNERS
new file mode 100644
index 000000000000..a742d0d22bf6
--- /dev/null
+++ b/nix/writeExecline/OWNERS
@@ -0,0 +1,3 @@
+inherited: true
+owners:
+  - Profpatsch
diff --git a/nix/writeExecline/default.nix b/nix/writeExecline/default.nix
new file mode 100644
index 000000000000..0916e2d58eac
--- /dev/null
+++ b/nix/writeExecline/default.nix
@@ -0,0 +1,67 @@
+{ pkgs, lib, ... }:
+
+# Write an execline script, represented as nested nix lists.
+# Everything is escaped correctly.
+# https://skarnet.org/software/execline/
+
+# TODO(Profpatsch) upstream into nixpkgs
+
+let
+  # replaces " and \ to \" and \\ respectively and quote with "
+  # e.g.
+  #   a"b\c -> "a\"b\\c"
+  #   a\"bc -> "a\\\"bc"
+  escapeExeclineArg = arg:
+    ''"${builtins.replaceStrings [ ''"'' ''\'' ] [ ''\"'' ''\\'' ] (toString arg)}"'';
+
+  # Escapes an execline (list of execline strings) to be passed to execlineb
+  # Give it a nested list of strings. Nested lists are interpolated as execline
+  # blocks ({}).
+  # Everything is quoted correctly.
+  #
+  # Example:
+  #   escapeExecline [ "if" [ "somecommand" ] "true" ]
+  #   == ''"if" { "somecommand" } "true"''
+  escapeExecline = execlineList: lib.concatStringsSep " "
+    (let
+      go = arg:
+        if      builtins.isString arg then [(escapeExeclineArg arg)]
+        else if builtins.isPath arg then [(escapeExeclineArg "${arg}")]
+        else if lib.isDerivation arg then [(escapeExeclineArg arg)]
+        else if builtins.isList arg then [ "{" ] ++ builtins.concatMap go arg ++ [ "}" ]
+        else abort "escapeExecline can only hande nested lists of strings, was ${lib.generators.toPretty {} arg}";
+     in builtins.concatMap go execlineList);
+
+in
+
+name:
+{
+  # "var": substitute readNArgs variables and start $@
+  # from the (readNArgs+1)th argument
+  # "var-full": substitute readNArgs variables and start $@ from $0
+  # "env": don’t substitute, set # and 0…n environment vaariables, where n=$#
+  # "none": don’t substitute or set any positional arguments
+  # "env-no-push": like "env", but bypass the push-phase. Not recommended.
+  argMode ? "var",
+  # Number of arguments to be substituted as variables (passed to "var"/"-s" or "var-full"/"-S"
+  readNArgs ? 0,
+}:
+# Nested list of lists of commands.
+# Inner lists are translated to execline blocks.
+argList:
+
+let
+  env =
+    if      argMode == "var" then "s${toString readNArgs}"
+    else if argMode == "var-full" then "S${toString readNArgs}"
+    else if argMode == "env" then ""
+    else if argMode == "none" then "P"
+    else if argMode == "env-no-push" then "p"
+    else abort ''"${toString argMode}" is not a valid argMode, use one of "var", "var-full", "env", "none", "env-no-push".'';
+
+in
+  # TODO(Profpatsch): rewrite `writeScript` with `runExecline`
+  pkgs.writeScript name ''
+    #!${pkgs.execline}/bin/execlineb -W${env}
+    ${escapeExecline argList}
+  ''