nix-helpers: c40be93aba7b1d7c2a1a48e7792486f0894b02d5

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

Generated by git2html.