nix-helpers: 354d283647fd14237f256706f60fee488f096217

     1: { bash, coreutils, lib, makeSetupHook, nixListToBashArray, repo1609, runCommand
     2: , stdenv, patchShebang, writeScript }:
     3: 
     4: with builtins;
     5: with lib;
     6: with rec {
     7:   # Load makeWrapper from 16.09 so that it has known behaviour w.r.t. quoting,
     8:   # etc.
     9:   makeWrapper = makeSetupHook { name = "make-wrapper"; }
    10:     "${repo1609}/pkgs/build-support/setup-hooks/make-wrapper.sh";
    11: };
    12: { file ? null, name, patchShebangs ? true, paths ? [ ], script ? null
    13: , vars ? { } }:
    14: assert file != null || script != null
    15:   || abort "wrap needs 'file' or 'script' argument";
    16: with rec {
    17:   # If we're given a string, write it to a file. We put that file in a
    18:   # directory since Python scripts can take a while to start if they live
    19:   # directly in the Nix store (presumably from scanning for modules).
    20:   inDir = runCommand "${name}-unwrapped" {
    21:     f = writeScript "${name}-raw"
    22:       (if patchShebangs then patchShebang { string = script; } else script);
    23:   } ''
    24:     mkdir "$out"
    25:     cp "$f" "$out/"${escapeShellArg name}
    26:   '';
    27: 
    28:   newFile = "${inDir}/${name}";
    29: 
    30:   f = if file == null then
    31:     newFile
    32:   else if patchShebangs then
    33:     patchShebang { inherit file name; }
    34:   else
    35:     file;
    36: 
    37:   # Whether any extra env vars or paths are actually needed
    38:   needEnv = if paths == [ ] && vars == { } then "false" else "true";
    39: 
    40:   # Store each path in a variable pathVarN
    41:   pathData = nixListToBashArray {
    42:     name = "pathVars";
    43:     args = paths;
    44:   };
    45: 
    46:   # Store each name in a variable varNamesN and the corresponding value in a
    47:   # variable varValsN. Their order is arbitrary, but must match up.
    48:   varNames = attrNames vars;
    49:   varNameData = nixListToBashArray {
    50:     name = "varNames";
    51:     args = varNames;
    52:   };
    53:   varValData = nixListToBashArray {
    54:     name = "varVals";
    55:     args = map (n: getAttr n vars) varNames;
    56:   };
    57: };
    58: runCommand name (pathData.env // varNameData.env // varValData.env // {
    59:   inherit f needEnv;
    60:   buildInputs = [ makeWrapper ];
    61: }) ''
    62:   # Shortcut if no extra env, etc. is needed
    63:   $needEnv || {
    64:     ln -s "$f" "$out"
    65:     exit
    66:   }
    67: 
    68:   ARGS=()
    69: 
    70:   ${pathData.code}
    71:   for P in "''${pathVars[@]}"
    72:   do
    73:     # Add $P/bin to $PATH
    74:     ARGS=("''${ARGS[@]}" "--prefix" "PATH" ":" "$P/bin")
    75: 
    76:     # We want 'paths' to act like 'buildInputs', so we also add any paths
    77:     # from 'propagated build inputs'
    78:     REMAINING=("$P/nix-support/propagated-native-build-inputs" "$P/nix-support/propagated-build-inputs" )
    79:     while [[ "''${#REMAINING[@]}" -gt 0 ]]
    80:     do
    81:       PROPS="''${REMAINING[0]}"
    82:       REMAINING=("''${REMAINING[@]:1:''${#REMAINING[@]}}" )
    83:       if [[ -e "$PROPS" ]]
    84:       then
    85:         while read -r PROP
    86:         do
    87:           ARGS=("''${ARGS[@]}" "--prefix" "PATH" ":" "$PROP/bin")
    88:           MORE="$PROP/nix-support/propagated-native-build-inputs"
    89:           if [[ -e "$MORE" ]]
    90:           then
    91:             REMAINING=("''${REMAINING[@]}" "$MORE")
    92:           fi
    93:           MORE="$PROP/nix-support/propagated-build-inputs"
    94:           if [[ -e "$MORE" ]]
    95:           then
    96:             REMAINING=("''${REMAINING[@]}" "$MORE")
    97:           fi
    98:         done < <(tr ' ' '\n' < "$PROPS")
    99:       fi
   100:     done
   101:   done
   102: 
   103:   ${varNameData.code}
   104:   ${varValData.code}
   105: 
   106:   # Loop through the indices of each name/value; this is slightly awkward
   107:   # since 'seq' likes to count from 1, but bash arrays start at 0.
   108:   for NPLUSONE in $(seq 1 "''${#varNames[@]}")
   109:   do
   110:     N=$(( NPLUSONE - 1 ))
   111: 
   112:     # makeWrapper doesn't escape properly, so spaces, quote marks, dollar
   113:     # signs, etc. will cause errors. Given a value FOO, makeWrapper will
   114:     # write out a script containing "FOO" (i.e. it wraps the text in
   115:     # double quotes). Double quotes aren't safe in Bash, since they splice
   116:     # in variables for dollar signs, etc. Plus, makeWrapper isn't actually
   117:     # doing any escaping: if our text contains a ", then it will appear
   118:     # verbatim and break the surrounding quotes.
   119:     # To work around this we do the following:
   120:     #  - Escape all single quotes in our value using sed; this is made
   121:     #    more awkward since we're using single-quoted Nix strings...
   122:     #  - Surround this escaped value in single quotes, hence making a
   123:     #    fully escaped text value which won't mess up any content
   124:     #  - Surround this single-quoted-and-escaped value in double quotes.
   125:     #    These "cancel out" the double quotes added by makeWrapper, i.e.
   126:     #    instead of FOO -> "FOO", we do "FOO" -> ""FOO"", and hence the
   127:     #    value FOO (in this case, our single-quoted-escaped-value) appears
   128:     #    OUTSIDE the double quotes, and is hence free to use single quotes
   129: 
   130:     # Pro tip to any readers: try to avoid unintended string
   131:     # interpretation wherever you can. Instead of "quoting variables where
   132:     # necessary", you should always quote all variables; instead of
   133:     # embedding raw strings into generated scripts and sprinkling around
   134:     # some quote marks, you should always escape them properly (in Bash,
   135:     # this is done by escaping single quotes wrapping in single quotes);
   136:     # never treat double quotes as an escaping mechanism.
   137: 
   138: 
   139:     # These vars make escaping slightly less crazy (Bash single-quote
   140:     # escaping requires adjacent single-quotes, but we're in a Nix string
   141:     # that's enclosed in double single-quotes... sigh)
   142:     BS='\'
   143:      T="'"
   144: 
   145:     ESC=$(echo "''${varVals[$N]}" | sed -e "s/$T/$T$BS$BS$T$T/g")
   146: 
   147:     ARGS=("''${ARGS[@]}" "--set" "''${varNames[$N]}" "\"'$ESC'\"")
   148:   done
   149: 
   150:   makeWrapper "$f" "$out" "''${ARGS[@]}"
   151: ''

Generated by git2html.