From 9a5b86c1894dba9c49dd0be91ec221ca3ac18e31 Mon Sep 17 00:00:00 2001 From: DavHau Date: Tue, 27 Feb 2024 21:56:15 +0700 Subject: [PATCH] writers: add support for wrapping Add a makeWrapperArgs argument to all script writers under pkgs.writers. This can be used to set, prefix, or suffix the PATH or other environment variables which improves the ability to generate scripts with reproducible behavior. Some of the writers (writeBash, writeDash, writeFish, writeNu) previously did not support passing an argument set, for example ``` writeBash "example" "echo hello" ``` In order to add the new capability to these writers as well, their call signature is now overloaded in order to allow the following: (The old call style from the example above remains intact) ``` writeBash "example" { makeWrapperArgs = [ "--prefix" "PATH" ":" "${pkgs.hello}/bin" ]; } '' hello '' ``` Done as well: - add tests - add more docs - fix some misleading docs - extend existing docs with more examples --- pkgs/build-support/writers/scripts.nix | 492 ++++++++++++++++++------- pkgs/build-support/writers/test.nix | 88 ++++- 2 files changed, 432 insertions(+), 148 deletions(-) diff --git a/pkgs/build-support/writers/scripts.nix b/pkgs/build-support/writers/scripts.nix index 8a23e5dd4a66..edc7ede4d5bd 100644 --- a/pkgs/build-support/writers/scripts.nix +++ b/pkgs/build-support/writers/scripts.nix @@ -1,4 +1,14 @@ -{ pkgs, buildPackages, lib, stdenv, libiconv, mkNugetDeps, mkNugetSource, gixy }: +{ + buildPackages, + gixy, + lib, + libiconv, + makeBinaryWrapper, + mkNugetDeps, + mkNugetSource, + pkgs, + stdenv, +}: let inherit (lib) concatMapStringsSep @@ -6,7 +16,6 @@ let escapeShellArg last optionalString - stringLength strings types ; @@ -18,137 +27,285 @@ rec { # Examples: # writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; } # makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world" - makeScriptWriter = { interpreter, check ? "" }: nameOrPath: content: + makeScriptWriter = { interpreter, check ? "", makeWrapperArgs ? [], }: nameOrPath: content: assert lib.or (types.path.check nameOrPath) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null); assert lib.or (types.path.check content) (types.str.check content); let + nameIsPath = types.path.check nameOrPath; name = last (builtins.split "/" nameOrPath); - in + path = if nameIsPath then nameOrPath else "/bin/${name}"; + # The inner derivation which creates the executable under $out/bin (never at $out directly) + # This is required in order to support wrapping, as wrapped programs consist of at least two files: the executable and the wrapper. + inner = + pkgs.runCommandLocal name ( + { + inherit makeWrapperArgs; + nativeBuildInputs = [ + makeBinaryWrapper + ]; + meta.mainProgram = name; + } + // ( + if (types.str.check content) then { + inherit content interpreter; + passAsFile = [ "content" ]; + } else { + inherit interpreter; + contentPath = content; + } + ) + ) + '' + # On darwin a script cannot be used as an interpreter in a shebang but + # there doesn't seem to be a limit to the size of shebang and multiple + # arguments to the interpreter are allowed. + if [[ -n "${toString pkgs.stdenvNoCC.isDarwin}" ]] && isScript $interpreter + then + wrapperInterpreterLine=$(head -1 "$interpreter" | tail -c+3) + # Get first word from the line (note: xargs echo remove leading spaces) + wrapperInterpreter=$(echo "$wrapperInterpreterLine" | xargs echo | cut -d " " -f1) - pkgs.runCommandLocal name ( - lib.optionalAttrs (nameOrPath == "/bin/${name}") { - meta.mainProgram = name; - } - // ( - if (types.str.check content) then { - inherit content interpreter; - passAsFile = [ "content" ]; - } else { - inherit interpreter; - contentPath = content; - } - ) - ) - '' - # On darwin a script cannot be used as an interpreter in a shebang but - # there doesn't seem to be a limit to the size of shebang and multiple - # arguments to the interpreter are allowed. - if [[ -n "${toString pkgs.stdenvNoCC.isDarwin}" ]] && isScript $interpreter - then - wrapperInterpreterLine=$(head -1 "$interpreter" | tail -c+3) - # Get first word from the line (note: xargs echo remove leading spaces) - wrapperInterpreter=$(echo "$wrapperInterpreterLine" | xargs echo | cut -d " " -f1) + if isScript $wrapperInterpreter + then + echo "error: passed interpreter ($interpreter) is a script which has another script ($wrapperInterpreter) as an interpreter, which is not supported." + exit 1 + fi - if isScript $wrapperInterpreter - then - echo "error: passed interpreter ($interpreter) is a script which has another script ($wrapperInterpreter) as an interpreter, which is not supported." - exit 1 - fi + # This should work as long as wrapperInterpreter is a shell, which is + # the case for programs wrapped with makeWrapper, like + # python3.withPackages etc. + interpreterLine="$wrapperInterpreterLine $interpreter" + else + interpreterLine=$interpreter + fi - # This should work as long as wrapperInterpreter is a shell, which is - # the case for programs wrapped with makeWrapper, like - # python3.withPackages etc. - interpreterLine="$wrapperInterpreterLine $interpreter" - else - interpreterLine=$interpreter - fi + echo "#! $interpreterLine" > $out + cat "$contentPath" >> $out + ${optionalString (check != "") '' + ${check} $out + ''} + chmod +x $out + + # Relocate executable + # Wrap it if makeWrapperArgs are specified + mv $out tmp + mkdir -p $out/$(dirname "${path}") + mv tmp $out/${path} + if [ -n "''${makeWrapperArgs+''${makeWrapperArgs[@]}}" ]; then + wrapProgram $out/${path} ''${makeWrapperArgs[@]} + fi + ''; + in + if nameIsPath + then inner + # In case nameOrPath is a name, the user intends the executable to be located at $out. + # This is achieved by creating a separate derivation containing a symlink at $out linking to ${inner}/bin/${name}. + # This breaks the override pattern. + # In case this turns out to be a problem, we can still add more magic + else pkgs.runCommandLocal name {} '' + ln -s ${inner}/bin/${name} $out + ''; - echo "#! $interpreterLine" > $out - cat "$contentPath" >> $out - ${optionalString (check != "") '' - ${check} $out - ''} - chmod +x $out - ${optionalString (types.path.check nameOrPath) '' - mv $out tmp - mkdir -p $out/$(dirname "${nameOrPath}") - mv tmp $out/${nameOrPath} - ''} - ''; # Base implementation for compiled executables. # Takes a compile script, which in turn takes the name as an argument. # # Examples: # writeSimpleC = makeBinWriter { compileScript = name: "gcc -o $out $contentPath"; } - makeBinWriter = { compileScript, strip ? true }: nameOrPath: content: + makeBinWriter = { compileScript, strip ? true, makeWrapperArgs ? [] }: nameOrPath: content: assert lib.or (types.path.check nameOrPath) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null); assert lib.or (types.path.check content) (types.str.check content); let + nameIsPath = types.path.check nameOrPath; name = last (builtins.split "/" nameOrPath); + path = if nameIsPath then nameOrPath else "/bin/${name}"; + # The inner derivation which creates the executable under $out/bin (never at $out directly) + # This is required in order to support wrapping, as wrapped programs consist of at least two files: the executable and the wrapper. + inner = + pkgs.runCommandLocal name ( + { + inherit makeWrapperArgs; + nativeBuildInputs = [ + makeBinaryWrapper + ]; + meta.mainProgram = name; + } + // ( + if (types.str.check content) then { + inherit content; + passAsFile = [ "content" ]; + } else { + contentPath = content; + } + ) + ) + '' + ${compileScript} + ${lib.optionalString strip + "${lib.getBin buildPackages.bintools-unwrapped}/bin/${buildPackages.bintools-unwrapped.targetPrefix}strip -S $out"} + # Sometimes binaries produced for darwin (e. g. by GHC) won't be valid + # mach-o executables from the get-go, but need to be corrected somehow + # which is done by fixupPhase. + ${lib.optionalString pkgs.stdenvNoCC.hostPlatform.isDarwin "fixupPhase"} + mv $out tmp + mkdir -p $out/$(dirname "${path}") + mv tmp $out/${path} + if [ -n "''${makeWrapperArgs+''${makeWrapperArgs[@]}}" ]; then + wrapProgram $out/${path} ''${makeWrapperArgs[@]} + fi + ''; in - pkgs.runCommand name ((if (types.str.check content) then { - inherit content; - passAsFile = [ "content" ]; - } else { - contentPath = content; - }) // lib.optionalAttrs (nameOrPath == "/bin/${name}") { - meta.mainProgram = name; - }) '' - ${compileScript} - ${lib.optionalString strip - "${lib.getBin buildPackages.bintools-unwrapped}/bin/${buildPackages.bintools-unwrapped.targetPrefix}strip -S $out"} - # Sometimes binaries produced for darwin (e. g. by GHC) won't be valid - # mach-o executables from the get-go, but need to be corrected somehow - # which is done by fixupPhase. - ${lib.optionalString pkgs.stdenvNoCC.hostPlatform.isDarwin "fixupPhase"} - ${optionalString (types.path.check nameOrPath) '' - mv $out tmp - mkdir -p $out/$(dirname "${nameOrPath}") - mv tmp $out/${nameOrPath} - ''} - ''; + if nameIsPath + then inner + # In case nameOrPath is a name, the user intends the executable to be located at $out. + # This is achieved by creating a separate derivation containing a symlink at $out linking to ${inner}/bin/${name}. + # This breaks the override pattern. + # In case this turns out to be a problem, we can still add more magic + else pkgs.runCommandLocal name {} '' + ln -s ${inner}/bin/${name} $out + ''; # Like writeScript but the first line is a shebang to bash # - # Example: + # Can be called with or without extra arguments. + # + # Example without arguments: # writeBash "example" '' # echo hello world # '' - writeBash = makeScriptWriter { - interpreter = "${lib.getExe pkgs.bash}"; - }; + # + # Example with arguments: + # writeBash "example" + # { + # makeWrapperArgs = [ + # "--prefix" "PATH" ":" "${pkgs.hello}/bin" + # ]; + # } + # '' + # hello + # '' + writeBash = name: argsOrScript: + if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript + then makeScriptWriter (argsOrScript // { interpreter = "${lib.getExe pkgs.bash}"; }) name + else makeScriptWriter { interpreter = "${lib.getExe pkgs.bash}"; } name argsOrScript; # Like writeScriptBin but the first line is a shebang to bash + # + # Can be called with or without extra arguments. + # + # Example without arguments: + # writeBashBin "example" '' + # echo hello world + # '' + # + # Example with arguments: + # writeBashBin "example" + # { + # makeWrapperArgs = [ + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", + # ]; + # } + # '' + # hello + # '' writeBashBin = name: writeBash "/bin/${name}"; # Like writeScript but the first line is a shebang to dash # - # Example: + # Can be called with or without extra arguments. + # + # Example without arguments: # writeDash "example" '' # echo hello world # '' - writeDash = makeScriptWriter { - interpreter = "${lib.getExe pkgs.dash}"; - }; + # + # Example with arguments: + # writeDash "example" + # { + # makeWrapperArgs = [ + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", + # ]; + # } + # '' + # hello + # '' + writeDash = name: argsOrScript: + if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript + then makeScriptWriter (argsOrScript // { interpreter = "${lib.getExe pkgs.dash}"; }) name + else makeScriptWriter { interpreter = "${lib.getExe pkgs.dash}"; } name argsOrScript; # Like writeScriptBin but the first line is a shebang to dash + # + # Can be called with or without extra arguments. + # + # Example without arguments: + # writeDashBin "example" '' + # echo hello world + # '' + # + # Example with arguments: + # writeDashBin "example" + # { + # makeWrapperArgs = [ + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", + # ]; + # } + # '' + # hello + # '' writeDashBin = name: writeDash "/bin/${name}"; # Like writeScript but the first line is a shebang to fish # - # Example: + # Can be called with or without extra arguments. + # + # Example without arguments: # writeFish "example" '' # echo hello world # '' - writeFish = makeScriptWriter { - interpreter = "${lib.getExe pkgs.fish} --no-config"; - check = "${lib.getExe pkgs.fish} --no-config --no-execute"; # syntax check only - }; + # + # Example with arguments: + # writeFish "example" + # { + # makeWrapperArgs = [ + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", + # ]; + # } + # '' + # hello + # '' + writeFish = name: argsOrScript: + if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript + then makeScriptWriter (argsOrScript // { + interpreter = "${lib.getExe pkgs.fish} --no-config"; + check = "${lib.getExe pkgs.fish} --no-config --no-execute"; # syntax check only + }) name + else makeScriptWriter { + interpreter = "${lib.getExe pkgs.fish} --no-config"; + check = "${lib.getExe pkgs.fish} --no-config --no-execute"; # syntax check only + } name argsOrScript; # Like writeScriptBin but the first line is a shebang to fish + # + # Can be called with or without extra arguments. + # + # Example without arguments: + # writeFishBin "example" '' + # echo hello world + # '' + # + # Example with arguments: + # writeFishBin "example" + # { + # makeWrapperArgs = [ + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", + # ]; + # } + # '' + # hello + # '' writeFishBin = name: writeFish "/bin/${name}"; @@ -162,11 +319,12 @@ rec { # main = launchMissiles # ''; writeHaskell = name: { - libraries ? [], ghc ? pkgs.ghc, ghcArgs ? [], + libraries ? [], + makeWrapperArgs ? [], + strip ? true, threadedRuntime ? true, - strip ? true }: let appendIfNotSet = el: list: if elem el list then list else list ++ [ el ]; @@ -178,7 +336,7 @@ rec { ${(ghc.withPackages (_: libraries ))}/bin/ghc ${lib.escapeShellArgs ghcArgs'} tmp.hs mv tmp $out ''; - inherit strip; + inherit makeWrapperArgs strip; } name; # writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin) @@ -187,36 +345,72 @@ rec { # Like writeScript but the first line is a shebang to nu # - # Example: + # Can be called with or without extra arguments. + # + # Example without arguments: # writeNu "example" '' # echo hello world # '' - writeNu = makeScriptWriter { - interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; - }; + # + # Example with arguments: + # writeNu "example" + # { + # makeWrapperArgs = [ + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", + # ]; + # } + # '' + # hello + # '' + writeNu = name: argsOrScript: + if lib.isAttrs argsOrScript && ! lib.isDerivation argsOrScript + then makeScriptWriter (argsOrScript // { interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; }) name + else makeScriptWriter { interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; } name argsOrScript; + # Like writeScriptBin but the first line is a shebang to nu + # + # Can be called with or without extra arguments. + # + # Example without arguments: + # writeNuBin "example" '' + # echo hello world + # '' + # + # Example with arguments: + # writeNuBin "example" + # { + # makeWrapperArgs = [ + # "--prefix", "PATH", ":", "${pkgs.hello}/bin", + # ]; + # } + # '' + # hello + # '' writeNuBin = name: writeNu "/bin/${name}"; # makeRubyWriter takes ruby and compatible rubyPackages and produces ruby script writer, # If any libraries are specified, ruby.withPackages is used as interpreter, otherwise the "bare" ruby is used. - makeRubyWriter = ruby: rubyPackages: buildRubyPackages: name: { libraries ? [], }: - makeScriptWriter { - interpreter = - if libraries == [] - then "${ruby}/bin/ruby" - else "${(ruby.withPackages (ps: libraries))}/bin/ruby"; - # Rubocop doesnt seem to like running in this fashion. - #check = (writeDash "rubocop.sh" '' - # exec ${lib.getExe buildRubyPackages.rubocop} "$1" - #''); - } name; + makeRubyWriter = ruby: rubyPackages: buildRubyPackages: name: { libraries ? [], ... } @ args: + makeScriptWriter ( + (builtins.removeAttrs args ["libraries"]) + // { + interpreter = + if libraries == [] + then "${ruby}/bin/ruby" + else "${(ruby.withPackages (ps: libraries))}/bin/ruby"; + # Rubocop doesn't seem to like running in this fashion. + #check = (writeDash "rubocop.sh" '' + # exec ${lib.getExe buildRubyPackages.rubocop} "$1" + #''); + } + ) name; # Like writeScript but the first line is a shebang to ruby # # Example: - # writeRuby "example" '' + # writeRuby "example" { libraries = [ pkgs.rubyPackages.git ]; } '' # puts "hello world" # '' writeRuby = makeRubyWriter pkgs.ruby pkgs.rubyPackages buildPackages.rubyPackages; @@ -227,17 +421,20 @@ rec { # makeLuaWriter takes lua and compatible luaPackages and produces lua script writer, # which validates the script with luacheck at build time. If any libraries are specified, # lua.withPackages is used as interpreter, otherwise the "bare" lua is used. - makeLuaWriter = lua: luaPackages: buildLuaPackages: name: { libraries ? [], }: - makeScriptWriter { - interpreter = lua.interpreter; - # if libraries == [] - # then lua.interpreter - # else (lua.withPackages (ps: libraries)).interpreter - # This should support packages! I just cant figure out why some dependency collision happens whenever I try to run this. - check = (writeDash "luacheck.sh" '' - exec ${buildLuaPackages.luacheck}/bin/luacheck "$1" - ''); - } name; + makeLuaWriter = lua: luaPackages: buildLuaPackages: name: { libraries ? [], ... } @ args: + makeScriptWriter ( + (builtins.removeAttrs args ["libraries"]) + // { + interpreter = lua.interpreter; + # if libraries == [] + # then lua.interpreter + # else (lua.withPackages (ps: libraries)).interpreter + # This should support packages! I just cant figure out why some dependency collision happens whenever I try to run this. + check = (writeDash "luacheck.sh" '' + exec ${buildLuaPackages.luacheck}/bin/luacheck "$1" + ''); + } + ) name; # writeLua takes a name an attributeset with libraries and some lua source code and # returns an executable (should also work with luajit) @@ -265,9 +462,10 @@ rec { writeLua "/bin/${name}"; writeRust = name: { - rustc ? pkgs.rustc, - rustcArgs ? [], - strip ? true + makeWrapperArgs ? [], + rustc ? pkgs.rustc, + rustcArgs ? [], + strip ? true, }: let darwinArgs = lib.optionals stdenv.isDarwin [ "-L${lib.getLib libiconv}/lib" ]; @@ -277,7 +475,7 @@ rec { cp "$contentPath" tmp.rs PATH=${lib.makeBinPath [pkgs.gcc]} ${rustc}/bin/rustc ${lib.escapeShellArgs rustcArgs} ${lib.escapeShellArgs darwinArgs} -o "$out" tmp.rs ''; - inherit strip; + inherit makeWrapperArgs strip; } name; writeRustBin = name: @@ -337,10 +535,13 @@ rec { # use boolean; # print "Howdy!\n" if true; # '' - writePerl = name: { libraries ? [] }: - makeScriptWriter { - interpreter = "${lib.getExe (pkgs.perl.withPackages (p: libraries))}"; - } name; + writePerl = name: { libraries ? [], ... } @ args: + makeScriptWriter ( + (builtins.removeAttrs args ["libraries"]) + // { + interpreter = "${lib.getExe (pkgs.perl.withPackages (p: libraries))}"; + } + ) name; # writePerlBin takes the same arguments as writePerl but outputs a directory (like writeScriptBin) writePerlBin = name: @@ -349,22 +550,27 @@ rec { # makePythonWriter takes python and compatible pythonPackages and produces python script writer, # which validates the script with flake8 at build time. If any libraries are specified, # python.withPackages is used as interpreter, otherwise the "bare" python is used. - makePythonWriter = python: pythonPackages: buildPythonPackages: name: { libraries ? [], flakeIgnore ? [] }: + makePythonWriter = python: pythonPackages: buildPythonPackages: name: { libraries ? [], flakeIgnore ? [], ... } @ args: let ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}"; in - makeScriptWriter { - interpreter = - if pythonPackages != pkgs.pypy2Packages || pythonPackages != pkgs.pypy3Packages then - if libraries == [] - then python.interpreter - else (python.withPackages (ps: libraries)).interpreter - else python.interpreter - ; - check = optionalString python.isPy3k (writeDash "pythoncheck.sh" '' - exec ${buildPythonPackages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1" - ''); - } name; + makeScriptWriter + ( + (builtins.removeAttrs args ["libraries" "flakeIgnore"]) + // { + interpreter = + if pythonPackages != pkgs.pypy2Packages || pythonPackages != pkgs.pypy3Packages then + if libraries == [] + then python.interpreter + else (python.withPackages (ps: libraries)).interpreter + else python.interpreter + ; + check = optionalString python.isPy3k (writeDash "pythoncheck.sh" '' + exec ${buildPythonPackages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1" + ''); + } + ) + name; # writePyPy2 takes a name an attributeset with libraries and some pypy2 sourcecode and # returns an executable @@ -421,7 +627,7 @@ rec { writePyPy3 "/bin/${name}"; - makeFSharpWriter = { dotnet-sdk ? pkgs.dotnet-sdk, fsi-flags ? "", libraries ? _: [] }: nameOrPath: + makeFSharpWriter = { dotnet-sdk ? pkgs.dotnet-sdk, fsi-flags ? "", libraries ? _: [], ... } @ args: nameOrPath: let fname = last (builtins.split "/" nameOrPath); path = if strings.hasSuffix ".fsx" nameOrPath then nameOrPath else "${nameOrPath}.fsx"; @@ -442,9 +648,12 @@ rec { ${lib.getExe dotnet-sdk} fsi --quiet --nologo --readline- ${fsi-flags} "$@" < "$script" ''; - in content: makeScriptWriter { - interpreter = fsi; - } path + in content: makeScriptWriter ( + (builtins.removeAttrs args ["dotnet-sdk" "fsi-flags" "libraries"]) + // { + interpreter = fsi; + } + ) path '' #i "nuget: ${nuget-source}/lib" ${ content } @@ -456,5 +665,4 @@ rec { writeFSharpBin = name: writeFSharp "/bin/${name}"; - } diff --git a/pkgs/build-support/writers/test.nix b/pkgs/build-support/writers/test.nix index 982c550d28e0..df0eb340d9ae 100644 --- a/pkgs/build-support/writers/test.nix +++ b/pkgs/build-support/writers/test.nix @@ -1,13 +1,8 @@ -{ glib -, haskellPackages +{ haskellPackages , lib , nodePackages , perlPackages -, pypy2Packages , python3Packages -, pypy3Packages -, luaPackages -, rubyPackages , runCommand , testers , writers @@ -310,4 +305,85 @@ lib.recurseIntoAttrs { expected = "hello: world\n"; }; }; + + wrapping = lib.recurseIntoAttrs { + bash-bin = expectSuccessBin ( + writeBashBin "test-writers-wrapping-bash-bin" + { + makeWrapperArgs = [ + "--set" + "ThaigerSprint" + "Thailand" + ]; + } + '' + if [[ "$ThaigerSprint" == "Thailand" ]]; then + echo "success" + fi + '' + ); + + bash = expectSuccess ( + writeBash "test-writers-wrapping-bash" + { + makeWrapperArgs = [ + "--set" + "ThaigerSprint" + "Thailand" + ]; + } + '' + if [[ "$ThaigerSprint" == "Thailand" ]]; then + echo "success" + fi + '' + ); + + python = expectSuccess ( + writePython3 "test-writers-wrapping-python" + { + makeWrapperArgs = [ + "--set" + "ThaigerSprint" + "Thailand" + ]; + } + '' + import os + + if os.environ.get("ThaigerSprint") == "Thailand": + print("success") + '' + ); + + rust = expectSuccess ( + writeRust "test-writers-wrapping-rust" + { + makeWrapperArgs = [ + "--set" + "ThaigerSprint" + "Thailand" + ]; + } + '' + fn main(){ + if std::env::var("ThaigerSprint").unwrap() == "Thailand" { + println!("success") + } + } + '' + ); + + no-empty-wrapper = let + bin = writeBashBin "bin" { makeWrapperArgs = []; } ''true''; + in runCommand "run-test-writers-wrapping-no-empty-wrapper" {} '' + ls -A ${bin}/bin + if [ $(ls -A ${bin}/bin | wc -l) -eq 1 ]; then + touch $out + else + echo "Error: Empty wrapper was created" >&2 + exit 1 + fi + ''; + }; }