Skip to content

Commit

Permalink
pkgs/profpatsch/runExecline: move to list
Browse files Browse the repository at this point in the history
We can auto-escape execlines correctly if we model them as nix-style
lists, so we shoud certainly do so. It also helps abstraction.
  • Loading branch information
Profpatsch committed Sep 26, 2019
1 parent e8738a0 commit 61dda87
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 46 deletions.
2 changes: 1 addition & 1 deletion pkgs/profpatsch/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ let
it = import ./execline/run-execline.nix {
bin = (bins execlineb-with-builtins [ "execlineb" ])
// (bins pkgs.execline [ "redirfd" "importas" "exec" ]);
inherit stdenv;
inherit stdenv lib;
};
itLocal = args: it (args // {
derivationArgs = args.derivationArgs or {} // {
Expand Down
30 changes: 30 additions & 0 deletions pkgs/profpatsch/execline/escape.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{ lib }:
let
# replaces " and \ to \" and \\ respectively and quote with "
# e.g.
# a"b\c -> "a\"b\\c"
# a\"bc -> "a\\\"bc"
# TODO upsteam into nixpkgs
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 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 {
inherit escapeExecline;
}
46 changes: 24 additions & 22 deletions pkgs/profpatsch/execline/run-execline-tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ let
preferLocalBuild = true;
allowSubstitutes = false;
};
execline = ''
importas -ui s scriptPath
importas -ui out out
foreground {
${coreutils}/bin/mv $s $out
}
${bin.s6-chmod} 0755 $out
'';
execline = [
"importas" "-ui" "s" "scriptPath"
"importas" "-ui" "out" "out"
"foreground" [
"${coreutils}/bin/mv" "$s" "$out"
]
"${bin.s6-chmod}" "0755" "$out"
];
};

# execline block of depth 1
Expand All @@ -43,15 +43,17 @@ let
bin.importas "-ui" "out" "out"
bin.s6-touch "$out"
];
preferLocalBuild = true;
allowSubstitutes = false;
};

# basic test that touches out
basic = runExecline {
name = "run-execline-test-basic";
execline = ''
importas -ui out out
${bin.s6-touch} $out
'';
execline = [
"importas" "-ui" "out" "out"
"${bin.s6-touch}" "$out"
];
derivationArgs = {
preferLocalBuild = true;
allowSubstitutes = false;
Expand All @@ -62,12 +64,12 @@ let
stdin = fileHasLine "foo" (runExecline {
name = "run-execline-test-stdin";
stdin = "foo\nbar\nfoo";
execline = ''
importas -ui out out
execline = [
"importas" "-ui" "out" "out"
# this pipes stdout of s6-cat to $out
# and s6-cat redirects from stdin to stdout
redirfd -w 1 $out ${bin.s6-cat}
'';
"redirfd" "-w" "1" "$out" bin.s6-cat
];
derivationArgs = {
preferLocalBuild = true;
allowSubstitutes = false;
Expand All @@ -80,12 +82,12 @@ let
#!${bin.execlineb} -S0
export myvar myvalue $@
'';
execline = ''
importas -ui v myvar
if { ${bin.s6-test} myvalue = $v }
importas out out
${bin.s6-touch} $out
'';
execline = [
"importas" "-ui" "v" "myvar"
"if" [ bin.s6-test "myvalue" "=" "$v" ]
"importas" "out" "out"
bin.s6-touch "$out"
];
derivationArgs = {
preferLocalBuild = true;
allowSubstitutes = false;
Expand Down
11 changes: 8 additions & 3 deletions pkgs/profpatsch/execline/run-execline.nix
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{ stdenv, bin }:
{ stdenv, bin, lib }:
{ name
# the execline script as string
# the execline script as a nested list of string,
# representing the blocks;
# see docs of `escapeExecline`.
, execline
# a string to pass as stdin to the execline script
, stdin ? ""
Expand All @@ -27,7 +29,10 @@ derivation (derivationArgs // {
# to pass the script and stdin as envvar;
# this might clash with another passed envar,
# so we give it a long & unique name
_runExeclineScript = execline;
_runExeclineScript =
let
escape = (import ./escape.nix { inherit lib; });
in escape.escapeExecline execline;
_runExeclineStdin = stdin;
passAsFile = [
"_runExeclineScript"
Expand Down
40 changes: 20 additions & 20 deletions pkgs/profpatsch/execline/symlink.nix
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,27 @@ runExecline {
PATH = lib.makeBinPath [ s6-portable-utils ];
};

execline = ''
importas -ui p pathTuplesPath
importas -ui out out
forbacktickx -d "" destorig { ${coreutils}/bin/cat $p }
importas -ui do destorig
multidefine -d "" $do { destsuffix orig }
define dest ''${out}/''${destsuffix}
execline = [
"importas" "-ui" "p" "pathTuplesPath"
"importas" "-ui" "out" "out"
"forbacktickx" "-d" "" "destorig" [ "${coreutils}/bin/cat" "$p" ]
"importas" "-ui" "do" "destorig"
"multidefine" "-d" "" "$do" [ "destsuffix" "orig" ]
"define" "dest" ''''${out}/''${destsuffix}''

# this call happens for every file, not very efficient
foreground {
backtick -n d { s6-dirname $dest }
importas -ui d d
s6-mkdir -p $d
}
"foreground" [
"backtick" "-n" "d" [ "s6-dirname" "$dest" ]
"importas" "-ui" "d" "d"
"s6-mkdir" "-p" "$d"
]

ifthenelse { s6-test -L $orig } {
backtick -n res { s6-linkname -f $orig }
importas -ui res res
s6-ln -fs $res $dest
} {
s6-ln -fs $orig $dest
}
'';
"ifthenelse" [ "s6-test" "-L" "$orig" ] [
"backtick" "-n" "res" [ "s6-linkname" "-f" "$orig" ]
"importas" "-ui" "res" "res"
"s6-ln" "-fs" "$res" "$dest"
] [
"s6-ln" "-fs" "$orig" "$dest"
]
];
}

0 comments on commit 61dda87

Please sign in to comment.