nix-helpers: 184ce603363b2aa8c634978dc2e2aedb59480919

     1: # Turns a list of Nix expressions into an attrset suitable for use in
     2: # derivations, WITHOUT forcing anything to be built at eval time. Also provides
     3: # a snippet of bash code to splice into scripts, which turns these env vars into
     4: # a proper bash array.
     5: #
     6: # For example, if we did:
     7: #
     8: #   nixListToBashArray { name = "x"; args = [ "bar", "baz", <derivation..> ]; }
     9: #
    10: # We will get back an 'env' attrset like this:
    11: #
    12: #   { x1 = "bar"; x2 = "baz"; x3 = <derivation...>; }
    13: #
    14: # Note that the derivation hasn't been forced; if we append this set to a build
    15: # environment, it will be treated like any other dependency. We also get a
    16: # 'code' snippet, which will loop over these distinct variables to construct a
    17: # single array (in this case, called "x")
    18: 
    19: # Take lib from nixpkgs1609, since other versions (e.g. 1603) mess up escaping
    20: {
    21:   nothing,
    22:   nixpkgs-lib,
    23:   runCommand,
    24: }:
    25: 
    26: with rec {
    27:   inherit (builtins)
    28:     attrNames
    29:     attrValues
    30:     foldl'
    31:     length
    32:     stringLength
    33:     toString
    34:     ;
    35:   inherit (nixpkgs-lib) escapeShellArg;
    36: 
    37:   # This is the actual implementation
    38:   go =
    39:     { args, name }:
    40:     rec {
    41:       # Store each arg in a separate variable, named numerically
    42:       env =
    43:         with foldl'
    44:           (result: arg: {
    45:             # Increment the count, for the next arg (if any)
    46:             count = result.count + 1;
    47: 
    48:             # Append a variable to the environment for this arg
    49:             vars = result.vars // {
    50:               "${name}${toString result.count}" = arg;
    51:             };
    52:           })
    53:           # Start with variable 1 in an empty environment
    54:           {
    55:             count = 1;
    56:             vars = { };
    57:           }
    58:           args;
    59:         vars;
    60: 
    61:       code = ''
    62:         ### Auto-generated by nixListToBashArray
    63:         ${name}=()
    64: 
    65:         for N in $(seq 1 "${toString (length args)}")
    66:         do
    67:           # Use a "variable variable" to look up "$name$N" as a variable name
    68:           NIXLISTTOBASHARRAYTEMP="${name}$N"
    69:           ${name}=("''${${name}[@]}" "''${!NIXLISTTOBASHARRAYTEMP}")
    70:           unset NIXLISTTOBASHARRAYTEMP
    71:         done
    72:         ### End of auto-generated code
    73:       '';
    74:     };
    75: 
    76:   ## Tests follow
    77: 
    78:   checkArgsAreNotForced =
    79:     with go {
    80:       name = "checkNotForced";
    81:       args = [ (abort "NLTBA shouldn't have forced") ];
    82:     };
    83:     # Force the code to be generated, and hence the content of any splices
    84:     (stringLength code > 0 || abort "No code generated")
    85:     &&
    86: 
    87:       # Force the environment "spine"/structure to be generated
    88:       (length (attrValues env) == 1 || abort "Wrong number of vars")
    89:     &&
    90: 
    91:       # Force the variable names (but not values!) to be generated
    92:       (attrNames env == [ "checkNotForced1" ] || abort "Wrong var name");
    93: 
    94:   checkWeGetTheRightValues =
    95:     with { mixture = ''mixture "of 'special" $characters''; };
    96:     with go {
    97:       name = "foo";
    98:       args = [
    99:         "simple"
   100:         "single 'quoted'"
   101:         ''double "quoted"''
   102:         mixture
   103:         nothing
   104:       ];
   105:     };
   106:     with rec {
   107:       context = env;
   108: 
   109:       check = runCommand "check-NLTBA.nix" context ''
   110:         function fail() {
   111:           echo -e "$*" 1>&2
   112:           exit 1
   113:         }
   114: 
   115:         function match() {
   116:           [[ "x$2" = "x$3" ]] || fail "Wrong $1 value '$2' (should be '$3')"
   117:         }
   118: 
   119:         match foo1 "$foo1" "simple"
   120:         match foo2 "$foo2" "single 'quoted'"
   121:         match foo3 "$foo3" 'double "quoted"'
   122:         match foo4 "$foo4" ${escapeShellArg mixture}
   123: 
   124:         [[ -d "$foo5" ]] || fail "foo5 should have been dir, got '$foo5'"
   125: 
   126:         ${code}
   127: 
   128:         [[ -n "$foo" ]] || fail "Didn't get foo: '$foo'"
   129: 
   130:         FOUND=0
   131:         for VAL in "''${foo[@]}"
   132:         do
   133:           FOUND=$(( FOUND + 1 ))
   134:         done
   135:         [[ "$FOUND" -eq 5 ]] || fail "foo has '$FOUND' elements"
   136: 
   137:         match 'foo[0]' "''${foo[0]}" "simple"
   138:         match 'foo[1]' "''${foo[1]}" "single 'quoted'"
   139:         match 'foo[2]' "''${foo[2]}" 'double "quoted"'
   140:         match 'foo[3]' "''${foo[3]}" ${escapeShellArg mixture}
   141: 
   142:         [[ -d "''${foo[4]}" ]] || fail "foo[4] should be dir, got: ''${foo[4]}"
   143: 
   144:         echo true > "$out"
   145:       '';
   146:     };
   147:     import check;
   148: };
   149: 
   150: assert checkArgsAreNotForced;
   151: assert checkWeGetTheRightValues;
   152: go

Generated by git2html.