Merge pull request #293420 from DavHau/writers-wrap

writers: add support for wrapping

authored by lassulus and committed by GitHub 7154ccbb 59dd321a

+432 -148
+350 -142
pkgs/build-support/writers/scripts.nix
··· 1 - { pkgs, buildPackages, lib, stdenv, libiconv, mkNugetDeps, mkNugetSource, gixy }: 2 let 3 inherit (lib) 4 concatMapStringsSep ··· 6 escapeShellArg 7 last 8 optionalString 9 - stringLength 10 strings 11 types 12 ; ··· 18 # Examples: 19 # writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; } 20 # makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world" 21 - makeScriptWriter = { interpreter, check ? "" }: nameOrPath: content: 22 assert lib.or (types.path.check nameOrPath) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null); 23 assert lib.or (types.path.check content) (types.str.check content); 24 let 25 name = last (builtins.split "/" nameOrPath); 26 - in 27 28 - pkgs.runCommandLocal name ( 29 - lib.optionalAttrs (nameOrPath == "/bin/${name}") { 30 - meta.mainProgram = name; 31 - } 32 - // ( 33 - if (types.str.check content) then { 34 - inherit content interpreter; 35 - passAsFile = [ "content" ]; 36 - } else { 37 - inherit interpreter; 38 - contentPath = content; 39 - } 40 - ) 41 - ) 42 - '' 43 - # On darwin a script cannot be used as an interpreter in a shebang but 44 - # there doesn't seem to be a limit to the size of shebang and multiple 45 - # arguments to the interpreter are allowed. 46 - if [[ -n "${toString pkgs.stdenvNoCC.isDarwin}" ]] && isScript $interpreter 47 - then 48 - wrapperInterpreterLine=$(head -1 "$interpreter" | tail -c+3) 49 - # Get first word from the line (note: xargs echo remove leading spaces) 50 - wrapperInterpreter=$(echo "$wrapperInterpreterLine" | xargs echo | cut -d " " -f1) 51 52 - if isScript $wrapperInterpreter 53 - then 54 - echo "error: passed interpreter ($interpreter) is a script which has another script ($wrapperInterpreter) as an interpreter, which is not supported." 55 - exit 1 56 - fi 57 58 - # This should work as long as wrapperInterpreter is a shell, which is 59 - # the case for programs wrapped with makeWrapper, like 60 - # python3.withPackages etc. 61 - interpreterLine="$wrapperInterpreterLine $interpreter" 62 - else 63 - interpreterLine=$interpreter 64 - fi 65 66 - echo "#! $interpreterLine" > $out 67 - cat "$contentPath" >> $out 68 - ${optionalString (check != "") '' 69 - ${check} $out 70 - ''} 71 - chmod +x $out 72 - ${optionalString (types.path.check nameOrPath) '' 73 - mv $out tmp 74 - mkdir -p $out/$(dirname "${nameOrPath}") 75 - mv tmp $out/${nameOrPath} 76 - ''} 77 - ''; 78 79 # Base implementation for compiled executables. 80 # Takes a compile script, which in turn takes the name as an argument. 81 # 82 # Examples: 83 # writeSimpleC = makeBinWriter { compileScript = name: "gcc -o $out $contentPath"; } 84 - makeBinWriter = { compileScript, strip ? true }: nameOrPath: content: 85 assert lib.or (types.path.check nameOrPath) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null); 86 assert lib.or (types.path.check content) (types.str.check content); 87 let 88 name = last (builtins.split "/" nameOrPath); 89 in 90 - pkgs.runCommand name ((if (types.str.check content) then { 91 - inherit content; 92 - passAsFile = [ "content" ]; 93 - } else { 94 - contentPath = content; 95 - }) // lib.optionalAttrs (nameOrPath == "/bin/${name}") { 96 - meta.mainProgram = name; 97 - }) '' 98 - ${compileScript} 99 - ${lib.optionalString strip 100 - "${lib.getBin buildPackages.bintools-unwrapped}/bin/${buildPackages.bintools-unwrapped.targetPrefix}strip -S $out"} 101 - # Sometimes binaries produced for darwin (e. g. by GHC) won't be valid 102 - # mach-o executables from the get-go, but need to be corrected somehow 103 - # which is done by fixupPhase. 104 - ${lib.optionalString pkgs.stdenvNoCC.hostPlatform.isDarwin "fixupPhase"} 105 - ${optionalString (types.path.check nameOrPath) '' 106 - mv $out tmp 107 - mkdir -p $out/$(dirname "${nameOrPath}") 108 - mv tmp $out/${nameOrPath} 109 - ''} 110 - ''; 111 112 # Like writeScript but the first line is a shebang to bash 113 # 114 - # Example: 115 # writeBash "example" '' 116 # echo hello world 117 # '' 118 - writeBash = makeScriptWriter { 119 - interpreter = "${lib.getExe pkgs.bash}"; 120 - }; 121 122 # Like writeScriptBin but the first line is a shebang to bash 123 writeBashBin = name: 124 writeBash "/bin/${name}"; 125 126 # Like writeScript but the first line is a shebang to dash 127 # 128 - # Example: 129 # writeDash "example" '' 130 # echo hello world 131 # '' 132 - writeDash = makeScriptWriter { 133 - interpreter = "${lib.getExe pkgs.dash}"; 134 - }; 135 136 # Like writeScriptBin but the first line is a shebang to dash 137 writeDashBin = name: 138 writeDash "/bin/${name}"; 139 140 # Like writeScript but the first line is a shebang to fish 141 # 142 - # Example: 143 # writeFish "example" '' 144 # echo hello world 145 # '' 146 - writeFish = makeScriptWriter { 147 - interpreter = "${lib.getExe pkgs.fish} --no-config"; 148 - check = "${lib.getExe pkgs.fish} --no-config --no-execute"; # syntax check only 149 - }; 150 151 # Like writeScriptBin but the first line is a shebang to fish 152 writeFishBin = name: 153 writeFish "/bin/${name}"; 154 ··· 162 # main = launchMissiles 163 # ''; 164 writeHaskell = name: { 165 - libraries ? [], 166 ghc ? pkgs.ghc, 167 ghcArgs ? [], 168 threadedRuntime ? true, 169 - strip ? true 170 }: 171 let 172 appendIfNotSet = el: list: if elem el list then list else list ++ [ el ]; ··· 178 ${(ghc.withPackages (_: libraries ))}/bin/ghc ${lib.escapeShellArgs ghcArgs'} tmp.hs 179 mv tmp $out 180 ''; 181 - inherit strip; 182 } name; 183 184 # writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin) ··· 187 188 # Like writeScript but the first line is a shebang to nu 189 # 190 - # Example: 191 # writeNu "example" '' 192 # echo hello world 193 # '' 194 - writeNu = makeScriptWriter { 195 - interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; 196 - }; 197 198 # Like writeScriptBin but the first line is a shebang to nu 199 writeNuBin = name: 200 writeNu "/bin/${name}"; 201 202 # makeRubyWriter takes ruby and compatible rubyPackages and produces ruby script writer, 203 # If any libraries are specified, ruby.withPackages is used as interpreter, otherwise the "bare" ruby is used. 204 - makeRubyWriter = ruby: rubyPackages: buildRubyPackages: name: { libraries ? [], }: 205 - makeScriptWriter { 206 - interpreter = 207 - if libraries == [] 208 - then "${ruby}/bin/ruby" 209 - else "${(ruby.withPackages (ps: libraries))}/bin/ruby"; 210 - # Rubocop doesnt seem to like running in this fashion. 211 - #check = (writeDash "rubocop.sh" '' 212 - # exec ${lib.getExe buildRubyPackages.rubocop} "$1" 213 - #''); 214 - } name; 215 216 # Like writeScript but the first line is a shebang to ruby 217 # 218 # Example: 219 - # writeRuby "example" '' 220 # puts "hello world" 221 # '' 222 writeRuby = makeRubyWriter pkgs.ruby pkgs.rubyPackages buildPackages.rubyPackages; ··· 227 # makeLuaWriter takes lua and compatible luaPackages and produces lua script writer, 228 # which validates the script with luacheck at build time. If any libraries are specified, 229 # lua.withPackages is used as interpreter, otherwise the "bare" lua is used. 230 - makeLuaWriter = lua: luaPackages: buildLuaPackages: name: { libraries ? [], }: 231 - makeScriptWriter { 232 - interpreter = lua.interpreter; 233 - # if libraries == [] 234 - # then lua.interpreter 235 - # else (lua.withPackages (ps: libraries)).interpreter 236 - # This should support packages! I just cant figure out why some dependency collision happens whenever I try to run this. 237 - check = (writeDash "luacheck.sh" '' 238 - exec ${buildLuaPackages.luacheck}/bin/luacheck "$1" 239 - ''); 240 - } name; 241 242 # writeLua takes a name an attributeset with libraries and some lua source code and 243 # returns an executable (should also work with luajit) ··· 265 writeLua "/bin/${name}"; 266 267 writeRust = name: { 268 - rustc ? pkgs.rustc, 269 - rustcArgs ? [], 270 - strip ? true 271 }: 272 let 273 darwinArgs = lib.optionals stdenv.isDarwin [ "-L${lib.getLib libiconv}/lib" ]; ··· 277 cp "$contentPath" tmp.rs 278 PATH=${lib.makeBinPath [pkgs.gcc]} ${rustc}/bin/rustc ${lib.escapeShellArgs rustcArgs} ${lib.escapeShellArgs darwinArgs} -o "$out" tmp.rs 279 ''; 280 - inherit strip; 281 } name; 282 283 writeRustBin = name: ··· 337 # use boolean; 338 # print "Howdy!\n" if true; 339 # '' 340 - writePerl = name: { libraries ? [] }: 341 - makeScriptWriter { 342 - interpreter = "${lib.getExe (pkgs.perl.withPackages (p: libraries))}"; 343 - } name; 344 345 # writePerlBin takes the same arguments as writePerl but outputs a directory (like writeScriptBin) 346 writePerlBin = name: ··· 349 # makePythonWriter takes python and compatible pythonPackages and produces python script writer, 350 # which validates the script with flake8 at build time. If any libraries are specified, 351 # python.withPackages is used as interpreter, otherwise the "bare" python is used. 352 - makePythonWriter = python: pythonPackages: buildPythonPackages: name: { libraries ? [], flakeIgnore ? [] }: 353 let 354 ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}"; 355 in 356 - makeScriptWriter { 357 - interpreter = 358 - if pythonPackages != pkgs.pypy2Packages || pythonPackages != pkgs.pypy3Packages then 359 - if libraries == [] 360 - then python.interpreter 361 - else (python.withPackages (ps: libraries)).interpreter 362 - else python.interpreter 363 - ; 364 - check = optionalString python.isPy3k (writeDash "pythoncheck.sh" '' 365 - exec ${buildPythonPackages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1" 366 - ''); 367 - } name; 368 369 # writePyPy2 takes a name an attributeset with libraries and some pypy2 sourcecode and 370 # returns an executable ··· 421 writePyPy3 "/bin/${name}"; 422 423 424 - makeFSharpWriter = { dotnet-sdk ? pkgs.dotnet-sdk, fsi-flags ? "", libraries ? _: [] }: nameOrPath: 425 let 426 fname = last (builtins.split "/" nameOrPath); 427 path = if strings.hasSuffix ".fsx" nameOrPath then nameOrPath else "${nameOrPath}.fsx"; ··· 442 ${lib.getExe dotnet-sdk} fsi --quiet --nologo --readline- ${fsi-flags} "$@" < "$script" 443 ''; 444 445 - in content: makeScriptWriter { 446 - interpreter = fsi; 447 - } path 448 '' 449 #i "nuget: ${nuget-source}/lib" 450 ${ content } ··· 456 457 writeFSharpBin = name: 458 writeFSharp "/bin/${name}"; 459 - 460 }
··· 1 + { 2 + buildPackages, 3 + gixy, 4 + lib, 5 + libiconv, 6 + makeBinaryWrapper, 7 + mkNugetDeps, 8 + mkNugetSource, 9 + pkgs, 10 + stdenv, 11 + }: 12 let 13 inherit (lib) 14 concatMapStringsSep ··· 16 escapeShellArg 17 last 18 optionalString 19 strings 20 types 21 ; ··· 27 # Examples: 28 # writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; } 29 # makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world" 30 + makeScriptWriter = { interpreter, check ? "", makeWrapperArgs ? [], }: nameOrPath: content: 31 assert lib.or (types.path.check nameOrPath) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null); 32 assert lib.or (types.path.check content) (types.str.check content); 33 let 34 + nameIsPath = types.path.check nameOrPath; 35 name = last (builtins.split "/" nameOrPath); 36 + path = if nameIsPath then nameOrPath else "/bin/${name}"; 37 + # The inner derivation which creates the executable under $out/bin (never at $out directly) 38 + # This is required in order to support wrapping, as wrapped programs consist of at least two files: the executable and the wrapper. 39 + inner = 40 + pkgs.runCommandLocal name ( 41 + { 42 + inherit makeWrapperArgs; 43 + nativeBuildInputs = [ 44 + makeBinaryWrapper 45 + ]; 46 + meta.mainProgram = name; 47 + } 48 + // ( 49 + if (types.str.check content) then { 50 + inherit content interpreter; 51 + passAsFile = [ "content" ]; 52 + } else { 53 + inherit interpreter; 54 + contentPath = content; 55 + } 56 + ) 57 + ) 58 + '' 59 + # On darwin a script cannot be used as an interpreter in a shebang but 60 + # there doesn't seem to be a limit to the size of shebang and multiple 61 + # arguments to the interpreter are allowed. 62 + if [[ -n "${toString pkgs.stdenvNoCC.isDarwin}" ]] && isScript $interpreter 63 + then 64 + wrapperInterpreterLine=$(head -1 "$interpreter" | tail -c+3) 65 + # Get first word from the line (note: xargs echo remove leading spaces) 66 + wrapperInterpreter=$(echo "$wrapperInterpreterLine" | xargs echo | cut -d " " -f1) 67 68 + if isScript $wrapperInterpreter 69 + then 70 + echo "error: passed interpreter ($interpreter) is a script which has another script ($wrapperInterpreter) as an interpreter, which is not supported." 71 + exit 1 72 + fi 73 74 + # This should work as long as wrapperInterpreter is a shell, which is 75 + # the case for programs wrapped with makeWrapper, like 76 + # python3.withPackages etc. 77 + interpreterLine="$wrapperInterpreterLine $interpreter" 78 + else 79 + interpreterLine=$interpreter 80 + fi 81 82 + echo "#! $interpreterLine" > $out 83 + cat "$contentPath" >> $out 84 + ${optionalString (check != "") '' 85 + ${check} $out 86 + ''} 87 + chmod +x $out 88 89 + # Relocate executable 90 + # Wrap it if makeWrapperArgs are specified 91 + mv $out tmp 92 + mkdir -p $out/$(dirname "${path}") 93 + mv tmp $out/${path} 94 + if [ -n "''${makeWrapperArgs+''${makeWrapperArgs[@]}}" ]; then 95 + wrapProgram $out/${path} ''${makeWrapperArgs[@]} 96 + fi 97 + ''; 98 + in 99 + if nameIsPath 100 + then inner 101 + # In case nameOrPath is a name, the user intends the executable to be located at $out. 102 + # This is achieved by creating a separate derivation containing a symlink at $out linking to ${inner}/bin/${name}. 103 + # This breaks the override pattern. 104 + # In case this turns out to be a problem, we can still add more magic 105 + else pkgs.runCommandLocal name {} '' 106 + ln -s ${inner}/bin/${name} $out 107 + ''; 108 + 109 110 # Base implementation for compiled executables. 111 # Takes a compile script, which in turn takes the name as an argument. 112 # 113 # Examples: 114 # writeSimpleC = makeBinWriter { compileScript = name: "gcc -o $out $contentPath"; } 115 + makeBinWriter = { compileScript, strip ? true, makeWrapperArgs ? [] }: nameOrPath: content: 116 assert lib.or (types.path.check nameOrPath) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null); 117 assert lib.or (types.path.check content) (types.str.check content); 118 let 119 + nameIsPath = types.path.check nameOrPath; 120 name = last (builtins.split "/" nameOrPath); 121 + path = if nameIsPath then nameOrPath else "/bin/${name}"; 122 + # The inner derivation which creates the executable under $out/bin (never at $out directly) 123 + # This is required in order to support wrapping, as wrapped programs consist of at least two files: the executable and the wrapper. 124 + inner = 125 + pkgs.runCommandLocal name ( 126 + { 127 + inherit makeWrapperArgs; 128 + nativeBuildInputs = [ 129 + makeBinaryWrapper 130 + ]; 131 + meta.mainProgram = name; 132 + } 133 + // ( 134 + if (types.str.check content) then { 135 + inherit content; 136 + passAsFile = [ "content" ]; 137 + } else { 138 + contentPath = content; 139 + } 140 + ) 141 + ) 142 + '' 143 + ${compileScript} 144 + ${lib.optionalString strip 145 + "${lib.getBin buildPackages.bintools-unwrapped}/bin/${buildPackages.bintools-unwrapped.targetPrefix}strip -S $out"} 146 + # Sometimes binaries produced for darwin (e. g. by GHC) won't be valid 147 + # mach-o executables from the get-go, but need to be corrected somehow 148 + # which is done by fixupPhase. 149 + ${lib.optionalString pkgs.stdenvNoCC.hostPlatform.isDarwin "fixupPhase"} 150 + mv $out tmp 151 + mkdir -p $out/$(dirname "${path}") 152 + mv tmp $out/${path} 153 + if [ -n "''${makeWrapperArgs+''${makeWrapperArgs[@]}}" ]; then 154 + wrapProgram $out/${path} ''${makeWrapperArgs[@]} 155 + fi 156 + ''; 157 in 158 + if nameIsPath 159 + then inner 160 + # In case nameOrPath is a name, the user intends the executable to be located at $out. 161 + # This is achieved by creating a separate derivation containing a symlink at $out linking to ${inner}/bin/${name}. 162 + # This breaks the override pattern. 163 + # In case this turns out to be a problem, we can still add more magic 164 + else pkgs.runCommandLocal name {} '' 165 + ln -s ${inner}/bin/${name} $out 166 + ''; 167 168 # Like writeScript but the first line is a shebang to bash 169 # 170 + # Can be called with or without extra arguments. 171 + # 172 + # Example without arguments: 173 # writeBash "example" '' 174 # echo hello world 175 # '' 176 + # 177 + # Example with arguments: 178 + # writeBash "example" 179 + # { 180 + # makeWrapperArgs = [ 181 + # "--prefix" "PATH" ":" "${pkgs.hello}/bin" 182 + # ]; 183 + # } 184 + # '' 185 + # hello 186 + # '' 187 + writeBash = name: argsOrScript: 188 + if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript 189 + then makeScriptWriter (argsOrScript // { interpreter = "${lib.getExe pkgs.bash}"; }) name 190 + else makeScriptWriter { interpreter = "${lib.getExe pkgs.bash}"; } name argsOrScript; 191 192 # Like writeScriptBin but the first line is a shebang to bash 193 + # 194 + # Can be called with or without extra arguments. 195 + # 196 + # Example without arguments: 197 + # writeBashBin "example" '' 198 + # echo hello world 199 + # '' 200 + # 201 + # Example with arguments: 202 + # writeBashBin "example" 203 + # { 204 + # makeWrapperArgs = [ 205 + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", 206 + # ]; 207 + # } 208 + # '' 209 + # hello 210 + # '' 211 writeBashBin = name: 212 writeBash "/bin/${name}"; 213 214 # Like writeScript but the first line is a shebang to dash 215 # 216 + # Can be called with or without extra arguments. 217 + # 218 + # Example without arguments: 219 # writeDash "example" '' 220 # echo hello world 221 # '' 222 + # 223 + # Example with arguments: 224 + # writeDash "example" 225 + # { 226 + # makeWrapperArgs = [ 227 + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", 228 + # ]; 229 + # } 230 + # '' 231 + # hello 232 + # '' 233 + writeDash = name: argsOrScript: 234 + if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript 235 + then makeScriptWriter (argsOrScript // { interpreter = "${lib.getExe pkgs.dash}"; }) name 236 + else makeScriptWriter { interpreter = "${lib.getExe pkgs.dash}"; } name argsOrScript; 237 238 # Like writeScriptBin but the first line is a shebang to dash 239 + # 240 + # Can be called with or without extra arguments. 241 + # 242 + # Example without arguments: 243 + # writeDashBin "example" '' 244 + # echo hello world 245 + # '' 246 + # 247 + # Example with arguments: 248 + # writeDashBin "example" 249 + # { 250 + # makeWrapperArgs = [ 251 + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", 252 + # ]; 253 + # } 254 + # '' 255 + # hello 256 + # '' 257 writeDashBin = name: 258 writeDash "/bin/${name}"; 259 260 # Like writeScript but the first line is a shebang to fish 261 # 262 + # Can be called with or without extra arguments. 263 + # 264 + # Example without arguments: 265 # writeFish "example" '' 266 # echo hello world 267 # '' 268 + # 269 + # Example with arguments: 270 + # writeFish "example" 271 + # { 272 + # makeWrapperArgs = [ 273 + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", 274 + # ]; 275 + # } 276 + # '' 277 + # hello 278 + # '' 279 + writeFish = name: argsOrScript: 280 + if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript 281 + then makeScriptWriter (argsOrScript // { 282 + interpreter = "${lib.getExe pkgs.fish} --no-config"; 283 + check = "${lib.getExe pkgs.fish} --no-config --no-execute"; # syntax check only 284 + }) name 285 + else makeScriptWriter { 286 + interpreter = "${lib.getExe pkgs.fish} --no-config"; 287 + check = "${lib.getExe pkgs.fish} --no-config --no-execute"; # syntax check only 288 + } name argsOrScript; 289 290 # Like writeScriptBin but the first line is a shebang to fish 291 + # 292 + # Can be called with or without extra arguments. 293 + # 294 + # Example without arguments: 295 + # writeFishBin "example" '' 296 + # echo hello world 297 + # '' 298 + # 299 + # Example with arguments: 300 + # writeFishBin "example" 301 + # { 302 + # makeWrapperArgs = [ 303 + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", 304 + # ]; 305 + # } 306 + # '' 307 + # hello 308 + # '' 309 writeFishBin = name: 310 writeFish "/bin/${name}"; 311 ··· 319 # main = launchMissiles 320 # ''; 321 writeHaskell = name: { 322 ghc ? pkgs.ghc, 323 ghcArgs ? [], 324 + libraries ? [], 325 + makeWrapperArgs ? [], 326 + strip ? true, 327 threadedRuntime ? true, 328 }: 329 let 330 appendIfNotSet = el: list: if elem el list then list else list ++ [ el ]; ··· 336 ${(ghc.withPackages (_: libraries ))}/bin/ghc ${lib.escapeShellArgs ghcArgs'} tmp.hs 337 mv tmp $out 338 ''; 339 + inherit makeWrapperArgs strip; 340 } name; 341 342 # writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin) ··· 345 346 # Like writeScript but the first line is a shebang to nu 347 # 348 + # Can be called with or without extra arguments. 349 + # 350 + # Example without arguments: 351 # writeNu "example" '' 352 # echo hello world 353 # '' 354 + # 355 + # Example with arguments: 356 + # writeNu "example" 357 + # { 358 + # makeWrapperArgs = [ 359 + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", 360 + # ]; 361 + # } 362 + # '' 363 + # hello 364 + # '' 365 + writeNu = name: argsOrScript: 366 + if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript 367 + then makeScriptWriter (argsOrScript // { interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; }) name 368 + else makeScriptWriter { interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; } name argsOrScript; 369 + 370 371 # Like writeScriptBin but the first line is a shebang to nu 372 + # 373 + # Can be called with or without extra arguments. 374 + # 375 + # Example without arguments: 376 + # writeNuBin "example" '' 377 + # echo hello world 378 + # '' 379 + # 380 + # Example with arguments: 381 + # writeNuBin "example" 382 + # { 383 + # makeWrapperArgs = [ 384 + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", 385 + # ]; 386 + # } 387 + # '' 388 + # hello 389 + # '' 390 writeNuBin = name: 391 writeNu "/bin/${name}"; 392 393 # makeRubyWriter takes ruby and compatible rubyPackages and produces ruby script writer, 394 # If any libraries are specified, ruby.withPackages is used as interpreter, otherwise the "bare" ruby is used. 395 + makeRubyWriter = ruby: rubyPackages: buildRubyPackages: name: { libraries ? [], ... } @ args: 396 + makeScriptWriter ( 397 + (builtins.removeAttrs args ["libraries"]) 398 + // { 399 + interpreter = 400 + if libraries == [] 401 + then "${ruby}/bin/ruby" 402 + else "${(ruby.withPackages (ps: libraries))}/bin/ruby"; 403 + # Rubocop doesn't seem to like running in this fashion. 404 + #check = (writeDash "rubocop.sh" '' 405 + # exec ${lib.getExe buildRubyPackages.rubocop} "$1" 406 + #''); 407 + } 408 + ) name; 409 410 # Like writeScript but the first line is a shebang to ruby 411 # 412 # Example: 413 + # writeRuby "example" { libraries = [ pkgs.rubyPackages.git ]; } '' 414 # puts "hello world" 415 # '' 416 writeRuby = makeRubyWriter pkgs.ruby pkgs.rubyPackages buildPackages.rubyPackages; ··· 421 # makeLuaWriter takes lua and compatible luaPackages and produces lua script writer, 422 # which validates the script with luacheck at build time. If any libraries are specified, 423 # lua.withPackages is used as interpreter, otherwise the "bare" lua is used. 424 + makeLuaWriter = lua: luaPackages: buildLuaPackages: name: { libraries ? [], ... } @ args: 425 + makeScriptWriter ( 426 + (builtins.removeAttrs args ["libraries"]) 427 + // { 428 + interpreter = lua.interpreter; 429 + # if libraries == [] 430 + # then lua.interpreter 431 + # else (lua.withPackages (ps: libraries)).interpreter 432 + # This should support packages! I just cant figure out why some dependency collision happens whenever I try to run this. 433 + check = (writeDash "luacheck.sh" '' 434 + exec ${buildLuaPackages.luacheck}/bin/luacheck "$1" 435 + ''); 436 + } 437 + ) name; 438 439 # writeLua takes a name an attributeset with libraries and some lua source code and 440 # returns an executable (should also work with luajit) ··· 462 writeLua "/bin/${name}"; 463 464 writeRust = name: { 465 + makeWrapperArgs ? [], 466 + rustc ? pkgs.rustc, 467 + rustcArgs ? [], 468 + strip ? true, 469 }: 470 let 471 darwinArgs = lib.optionals stdenv.isDarwin [ "-L${lib.getLib libiconv}/lib" ]; ··· 475 cp "$contentPath" tmp.rs 476 PATH=${lib.makeBinPath [pkgs.gcc]} ${rustc}/bin/rustc ${lib.escapeShellArgs rustcArgs} ${lib.escapeShellArgs darwinArgs} -o "$out" tmp.rs 477 ''; 478 + inherit makeWrapperArgs strip; 479 } name; 480 481 writeRustBin = name: ··· 535 # use boolean; 536 # print "Howdy!\n" if true; 537 # '' 538 + writePerl = name: { libraries ? [], ... } @ args: 539 + makeScriptWriter ( 540 + (builtins.removeAttrs args ["libraries"]) 541 + // { 542 + interpreter = "${lib.getExe (pkgs.perl.withPackages (p: libraries))}"; 543 + } 544 + ) name; 545 546 # writePerlBin takes the same arguments as writePerl but outputs a directory (like writeScriptBin) 547 writePerlBin = name: ··· 550 # makePythonWriter takes python and compatible pythonPackages and produces python script writer, 551 # which validates the script with flake8 at build time. If any libraries are specified, 552 # python.withPackages is used as interpreter, otherwise the "bare" python is used. 553 + makePythonWriter = python: pythonPackages: buildPythonPackages: name: { libraries ? [], flakeIgnore ? [], ... } @ args: 554 let 555 ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}"; 556 in 557 + makeScriptWriter 558 + ( 559 + (builtins.removeAttrs args ["libraries" "flakeIgnore"]) 560 + // { 561 + interpreter = 562 + if pythonPackages != pkgs.pypy2Packages || pythonPackages != pkgs.pypy3Packages then 563 + if libraries == [] 564 + then python.interpreter 565 + else (python.withPackages (ps: libraries)).interpreter 566 + else python.interpreter 567 + ; 568 + check = optionalString python.isPy3k (writeDash "pythoncheck.sh" '' 569 + exec ${buildPythonPackages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1" 570 + ''); 571 + } 572 + ) 573 + name; 574 575 # writePyPy2 takes a name an attributeset with libraries and some pypy2 sourcecode and 576 # returns an executable ··· 627 writePyPy3 "/bin/${name}"; 628 629 630 + makeFSharpWriter = { dotnet-sdk ? pkgs.dotnet-sdk, fsi-flags ? "", libraries ? _: [], ... } @ args: nameOrPath: 631 let 632 fname = last (builtins.split "/" nameOrPath); 633 path = if strings.hasSuffix ".fsx" nameOrPath then nameOrPath else "${nameOrPath}.fsx"; ··· 648 ${lib.getExe dotnet-sdk} fsi --quiet --nologo --readline- ${fsi-flags} "$@" < "$script" 649 ''; 650 651 + in content: makeScriptWriter ( 652 + (builtins.removeAttrs args ["dotnet-sdk" "fsi-flags" "libraries"]) 653 + // { 654 + interpreter = fsi; 655 + } 656 + ) path 657 '' 658 #i "nuget: ${nuget-source}/lib" 659 ${ content } ··· 665 666 writeFSharpBin = name: 667 writeFSharp "/bin/${name}"; 668 }
+82 -6
pkgs/build-support/writers/test.nix
··· 1 - { glib 2 - , haskellPackages 3 , lib 4 , nodePackages 5 , perlPackages 6 - , pypy2Packages 7 , python3Packages 8 - , pypy3Packages 9 - , luaPackages 10 - , rubyPackages 11 , runCommand 12 , testers 13 , writers ··· 309 file = writeYAML "data.yaml" { hello = "world"; }; 310 expected = "hello: world\n"; 311 }; 312 }; 313 }
··· 1 + { haskellPackages 2 , lib 3 , nodePackages 4 , perlPackages 5 , python3Packages 6 , runCommand 7 , testers 8 , writers ··· 304 file = writeYAML "data.yaml" { hello = "world"; }; 305 expected = "hello: world\n"; 306 }; 307 + }; 308 + 309 + wrapping = lib.recurseIntoAttrs { 310 + bash-bin = expectSuccessBin ( 311 + writeBashBin "test-writers-wrapping-bash-bin" 312 + { 313 + makeWrapperArgs = [ 314 + "--set" 315 + "ThaigerSprint" 316 + "Thailand" 317 + ]; 318 + } 319 + '' 320 + if [[ "$ThaigerSprint" == "Thailand" ]]; then 321 + echo "success" 322 + fi 323 + '' 324 + ); 325 + 326 + bash = expectSuccess ( 327 + writeBash "test-writers-wrapping-bash" 328 + { 329 + makeWrapperArgs = [ 330 + "--set" 331 + "ThaigerSprint" 332 + "Thailand" 333 + ]; 334 + } 335 + '' 336 + if [[ "$ThaigerSprint" == "Thailand" ]]; then 337 + echo "success" 338 + fi 339 + '' 340 + ); 341 + 342 + python = expectSuccess ( 343 + writePython3 "test-writers-wrapping-python" 344 + { 345 + makeWrapperArgs = [ 346 + "--set" 347 + "ThaigerSprint" 348 + "Thailand" 349 + ]; 350 + } 351 + '' 352 + import os 353 + 354 + if os.environ.get("ThaigerSprint") == "Thailand": 355 + print("success") 356 + '' 357 + ); 358 + 359 + rust = expectSuccess ( 360 + writeRust "test-writers-wrapping-rust" 361 + { 362 + makeWrapperArgs = [ 363 + "--set" 364 + "ThaigerSprint" 365 + "Thailand" 366 + ]; 367 + } 368 + '' 369 + fn main(){ 370 + if std::env::var("ThaigerSprint").unwrap() == "Thailand" { 371 + println!("success") 372 + } 373 + } 374 + '' 375 + ); 376 + 377 + no-empty-wrapper = let 378 + bin = writeBashBin "bin" { makeWrapperArgs = []; } ''true''; 379 + in runCommand "run-test-writers-wrapping-no-empty-wrapper" {} '' 380 + ls -A ${bin}/bin 381 + if [ $(ls -A ${bin}/bin | wc -l) -eq 1 ]; then 382 + touch $out 383 + else 384 + echo "Error: Empty wrapper was created" >&2 385 + exit 1 386 + fi 387 + ''; 388 }; 389 }