nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at python-updates 290 lines 7.5 kB view raw
1{ 2 lib, 3 stdenv, 4 resholve, 5 binlore, 6 writeTextFile, 7}: 8 9rec { 10 /* 11 These functions break up the work of partially validating the 12 'solutions' attrset and massaging it into env/cli args. 13 14 Note: some of the left-most args do not *have* to be passed as 15 deep as they are, but I've done so to provide more error context 16 */ 17 18 # for brevity / line length 19 spaces = l: builtins.concatStringsSep " " l; 20 colons = l: builtins.concatStringsSep ":" l; 21 semicolons = l: builtins.concatStringsSep ";" l; 22 23 # Throw a fit with dotted attr path context 24 nope = path: msg: throw "${builtins.concatStringsSep "." path}: ${msg}"; 25 26 # Special-case directive value representations by type 27 phraseDirective = 28 solution: env: name: val: 29 if builtins.isInt val then 30 toString val 31 else if builtins.isString val then 32 name 33 else if true == val then 34 name 35 else if false == val then 36 "" # omit! 37 else if null == val then 38 "" # omit! 39 else if builtins.isList val then 40 "${name}:${semicolons (map lib.escapeShellArg val)}" 41 else 42 nope [ 43 solution 44 env 45 name 46 ] "unexpected type: ${builtins.typeOf val}"; 47 48 # Build fake/fix/keep directives from Nix types 49 phraseDirectives = 50 solution: env: val: 51 lib.mapAttrsToList (phraseDirective solution env) val; 52 53 # Custom ~search-path routine to handle relative path strings 54 relSafeBinPath = 55 input: 56 if lib.isDerivation input then 57 ((lib.getOutput "bin" input) + "/bin") 58 else if builtins.isString input then 59 input 60 else 61 throw "unexpected type for input: ${builtins.typeOf input}"; 62 63 # Special-case value representation by type/name 64 phraseEnvVal = 65 solution: env: val: 66 if env == "inputs" then 67 (colons (map relSafeBinPath val)) 68 else if builtins.isString val then 69 val 70 else if builtins.isList val then 71 spaces val 72 else if builtins.isAttrs val then 73 spaces (phraseDirectives solution env val) 74 else 75 nope [ 76 solution 77 env 78 ] "unexpected type: ${builtins.typeOf val}"; 79 80 # Shell-format each env value 81 shellEnv = 82 solution: env: value: 83 lib.escapeShellArg (phraseEnvVal solution env value); 84 85 # Build a single ENV=val pair 86 phraseEnv = 87 solution: env: value: 88 "RESHOLVE_${lib.toUpper env}=${shellEnv solution env value}"; 89 90 /* 91 Discard attrs: 92 - claimed by phraseArgs 93 - only needed for binlore.collect 94 */ 95 removeUnneededArgs = 96 value: 97 removeAttrs value [ 98 "scripts" 99 "flags" 100 "unresholved" 101 ]; 102 103 # Verify required arguments are present 104 validateSolution = 105 { 106 scripts, 107 inputs, 108 interpreter, 109 ... 110 }: 111 true; 112 113 # Pull out specific solution keys to build ENV=val pairs 114 phraseEnvs = 115 solution: value: spaces (lib.mapAttrsToList (phraseEnv solution) (removeUnneededArgs value)); 116 117 # Pull out specific solution keys to build CLI argstring 118 phraseArgs = 119 { 120 flags ? [ ], 121 scripts, 122 ... 123 }: 124 spaces (flags ++ scripts); 125 126 phraseBinloreArgs = 127 value: 128 let 129 hasUnresholved = builtins.hasAttr "unresholved" value; 130 in 131 { 132 drvs = value.inputs ++ lib.optionals hasUnresholved [ value.unresholved ]; 133 strip = if hasUnresholved then [ value.unresholved ] else [ ]; 134 }; 135 136 # Build a single resholve invocation 137 phraseInvocation = 138 solution: value: 139 if validateSolution value then 140 # we pass resholve a directory 141 "RESHOLVE_LORE=${binlore.collect (phraseBinloreArgs value)} ${phraseEnvs solution value} ${resholve}/bin/resholve --overwrite ${phraseArgs value}" 142 else 143 throw "invalid solution"; # shouldn't trigger for now 144 145 injectUnresholved = 146 solutions: unresholved: 147 (builtins.mapAttrs (name: value: value // { inherit unresholved; }) solutions); 148 149 # Build resholve invocation for each solution. 150 phraseCommands = 151 solutions: unresholved: 152 builtins.concatStringsSep "\n" ( 153 lib.mapAttrsToList phraseInvocation (injectUnresholved solutions unresholved) 154 ); 155 156 /* 157 subshell/PS4/set -x and : command to output resholve envs 158 and invocation. Extra context makes it clearer what the 159 Nix API is doing, makes nix-shell debugging easier, etc. 160 */ 161 phraseContext = 162 { 163 invokable, 164 prep ? ''cd "$out"'', 165 }: 166 '' 167 ( 168 ${prep} 169 PS4=$'\x1f'"\033[33m[resholve context]\033[0m " 170 set -x 171 : invoking resholve with PWD=$PWD 172 ${invokable} 173 ) 174 ''; 175 phraseContextForPWD = 176 invokable: 177 phraseContext { 178 inherit invokable; 179 prep = ""; 180 }; 181 phraseContextForOut = invokable: phraseContext { inherit invokable; }; 182 183 phraseSolution = name: solution: (phraseContextForOut (phraseInvocation name solution)); 184 phraseSolutions = 185 solutions: unresholved: phraseContextForOut (phraseCommands solutions unresholved); 186 187 writeScript = 188 name: partialSolution: text: 189 writeTextFile { 190 inherit name text; 191 executable = true; 192 checkPhase = '' 193 ${ 194 (phraseContextForPWD ( 195 phraseInvocation name ( 196 partialSolution 197 // { 198 scripts = [ "${placeholder "out"}" ]; 199 } 200 ) 201 )) 202 } 203 ''; 204 }; 205 writeScriptBin = 206 name: partialSolution: text: 207 writeTextFile rec { 208 inherit name text; 209 executable = true; 210 destination = "/bin/${name}"; 211 checkPhase = '' 212 ${phraseContextForOut ( 213 phraseInvocation name ( 214 partialSolution 215 // { 216 scripts = [ "bin/${name}" ]; 217 } 218 ) 219 )} 220 ''; 221 }; 222 mkDerivation = 223 { 224 pname, 225 src, 226 version, 227 passthru ? { }, 228 solutions, 229 postResholve ? "", 230 ... 231 }@attrs: 232 let 233 inherit stdenv; 234 235 /* 236 Knock out our special solutions arg, but otherwise 237 just build what the caller is giving us. We'll 238 actually resholve it separately below (after we 239 generate binlore for it). 240 */ 241 unresholved = ( 242 stdenv.mkDerivation ( 243 (removeAttrs attrs [ "solutions" ]) 244 // { 245 inherit version src; 246 pname = "${pname}-unresholved"; 247 } 248 ) 249 ); 250 in 251 /* 252 resholve in a separate derivation; some concerns: 253 - we aren't keeping many of the user's args, so they 254 can't readily set LOGLEVEL and such... 255 - not sure how this affects multiple outputs 256 */ 257 lib.extendDerivation true passthru ( 258 stdenv.mkDerivation { 259 src = unresholved; 260 inherit version pname; 261 buildInputs = [ resholve ]; 262 disallowedReferences = [ resholve ]; 263 264 # retain a reference to the base 265 passthru = unresholved.passthru // { 266 unresholved = unresholved; 267 # fallback attr for update bot to query our src 268 originalSrc = unresholved.src; 269 }; 270 271 # do these imply that we should use NoCC or something? 272 dontConfigure = true; 273 dontBuild = true; 274 275 installPhase = '' 276 cp -R $src $out 277 ''; 278 279 # enable below for verbose debug info if needed 280 # supports default python.logging levels 281 # LOGLEVEL="INFO"; 282 preFixup = phraseSolutions solutions unresholved; 283 284 postFixup = postResholve; 285 286 # don't break the metadata... 287 meta = unresholved.meta; 288 } 289 ); 290}