From d0c7ffc596bd8da9298d693d1bdd0559c6777311 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Sat, 22 Jul 2023 13:48:25 +0200 Subject: [PATCH] writers: make room for other types of writers --- pkgs/build-support/writers/default.nix | 364 +---------------------- pkgs/build-support/writers/scripts.nix | 383 +++++++++++++++++++++++++ 2 files changed, 386 insertions(+), 361 deletions(-) create mode 100644 pkgs/build-support/writers/scripts.nix diff --git a/pkgs/build-support/writers/default.nix b/pkgs/build-support/writers/default.nix index faac553dee3f..c26ada8bc571 100644 --- a/pkgs/build-support/writers/default.nix +++ b/pkgs/build-support/writers/default.nix @@ -1,368 +1,10 @@ -{ pkgs, config, buildPackages, lib, stdenv, libiconv, mkNugetDeps, mkNugetSource, gixy }: +{ pkgs, config, lib }: let aliases = if config.allowAliases then (import ./aliases.nix lib) else prev: {}; - writers = with lib; rec { - # Base implementation for non-compiled executables. - # Takes an interpreter, for example `${pkgs.bash}/bin/bash` - # - # Examples: - # writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; } - # makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world" - makeScriptWriter = { interpreter, check ? "" }: 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 - name = last (builtins.split "/" nameOrPath); - in + scriptWriters = import ./scripts.nix { inherit pkgs lib; }; - pkgs.runCommandLocal 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 - - # 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 - ${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: - 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 - name = last (builtins.split "/" nameOrPath); - in - pkgs.runCommand name ((if (types.str.check content) then { - inherit content; - passAsFile = [ "content" ]; - } else { - contentPath = content; - }) // lib.optionalAttrs (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64) { - # post-link-hook expects codesign_allocate to be in PATH - # https://github.com/NixOS/nixpkgs/issues/154203 - # https://github.com/NixOS/nixpkgs/issues/148189 - nativeBuildInputs = [ stdenv.cc.bintools ]; - }) '' - ${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} - ''} - ''; - - # Like writeScript but the first line is a shebang to bash - # - # Example: - # writeBash "example" '' - # echo hello world - # '' - writeBash = makeScriptWriter { - interpreter = "${pkgs.bash}/bin/bash"; - }; - - # Like writeScriptBin but the first line is a shebang to bash - writeBashBin = name: - writeBash "/bin/${name}"; - - # Like writeScript but the first line is a shebang to dash - # - # Example: - # writeDash "example" '' - # echo hello world - # '' - writeDash = makeScriptWriter { - interpreter = "${pkgs.dash}/bin/dash"; - }; - - # Like writeScriptBin but the first line is a shebang to dash - writeDashBin = name: - writeDash "/bin/${name}"; - - # Like writeScript but the first line is a shebang to fish - # - # Example: - # writeFish "example" '' - # echo hello world - # '' - writeFish = makeScriptWriter { - interpreter = "${pkgs.fish}/bin/fish --no-config"; - check = "${pkgs.fish}/bin/fish --no-config --no-execute"; # syntax check only - }; - - # Like writeScriptBin but the first line is a shebang to fish - writeFishBin = name: - writeFish "/bin/${name}"; - - # writeHaskell takes a name, an attrset with libraries and haskell version (both optional) - # and some haskell source code and returns an executable. - # - # Example: - # writeHaskell "missiles" { libraries = [ pkgs.haskellPackages.acme-missiles ]; } '' - # import Acme.Missiles - # - # main = launchMissiles - # ''; - writeHaskell = name: { - libraries ? [], - ghc ? pkgs.ghc, - ghcArgs ? [], - threadedRuntime ? true, - strip ? true - }: - let - appendIfNotSet = el: list: if elem el list then list else list ++ [ el ]; - ghcArgs' = if threadedRuntime then appendIfNotSet "-threaded" ghcArgs else ghcArgs; - - in makeBinWriter { - compileScript = '' - cp $contentPath tmp.hs - ${ghc.withPackages (_: libraries )}/bin/ghc ${lib.escapeShellArgs ghcArgs'} tmp.hs - mv tmp $out - ''; - inherit strip; - } name; - - # writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin) - writeHaskellBin = name: - writeHaskell "/bin/${name}"; - - writeRust = name: { - rustc ? pkgs.rustc, - rustcArgs ? [], - strip ? true - }: - let - darwinArgs = lib.optionals stdenv.isDarwin [ "-L${lib.getLib libiconv}/lib" ]; - in - makeBinWriter { - compileScript = '' - cp "$contentPath" tmp.rs - PATH=${makeBinPath [pkgs.gcc]} ${lib.getBin rustc}/bin/rustc ${lib.escapeShellArgs rustcArgs} ${lib.escapeShellArgs darwinArgs} -o "$out" tmp.rs - ''; - inherit strip; - } name; - - writeRustBin = name: - writeRust "/bin/${name}"; - - # writeJS takes a name an attributeset with libraries and some JavaScript sourcecode and - # returns an executable - # - # Example: - # writeJS "example" { libraries = [ pkgs.nodePackages.uglify-js ]; } '' - # var UglifyJS = require("uglify-js"); - # var code = "function add(first, second) { return first + second; }"; - # var result = UglifyJS.minify(code); - # console.log(result.code); - # '' - writeJS = name: { libraries ? [] }: content: - let - node-env = pkgs.buildEnv { - name = "node"; - paths = libraries; - pathsToLink = [ - "/lib/node_modules" - ]; - }; - in writeDash name '' - export NODE_PATH=${node-env}/lib/node_modules - exec ${pkgs.nodejs}/bin/node ${pkgs.writeText "js" content} "$@" - ''; - - # writeJSBin takes the same arguments as writeJS but outputs a directory (like writeScriptBin) - writeJSBin = name: - writeJS "/bin/${name}"; - - awkFormatNginx = builtins.toFile "awkFormat-nginx.awk" '' - awk -f - {sub(/^[ \t]+/,"");idx=0} - /\{/{ctx++;idx=1} - /\}/{ctx--} - {id="";for(i=idx;i $out - gixy $out - ''; - - # writePerl takes a name an attributeset with libraries and some perl sourcecode and - # returns an executable - # - # Example: - # writePerl "example" { libraries = [ pkgs.perlPackages.boolean ]; } '' - # use boolean; - # print "Howdy!\n" if true; - # '' - writePerl = name: { libraries ? [] }: - makeScriptWriter { - interpreter = "${pkgs.perl.withPackages (p: libraries)}/bin/perl"; - } name; - - # writePerlBin takes the same arguments as writePerl but outputs a directory (like writeScriptBin) - writePerlBin = name: - writePerl "/bin/${name}"; - - # 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 ? [] }: - let - ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}"; - in - makeScriptWriter { - interpreter = - if libraries == [] - then python.interpreter - else (python.withPackages (ps: libraries)).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 - # - # Example: - # writePyPy2 "test_pypy2" { libraries = [ pkgs.pypy2Packages.enum ]; } '' - # from enum import Enum - # - # class Test(Enum): - # a = "success" - # - # print Test.a - # '' - writePyPy2 = makePythonWriter pkgs.pypy2 pkgs.pypy2Packages buildPackages.pypy2Packages; - - # writePyPy2Bin takes the same arguments as writePyPy2 but outputs a directory (like writeScriptBin) - writePyPy2Bin = name: - writePyPy2 "/bin/${name}"; - - # writePython3 takes a name an attributeset with libraries and some python3 sourcecode and - # returns an executable - # - # Example: - # writePython3 "test_python3" { libraries = [ pkgs.python3Packages.pyyaml ]; } '' - # import yaml - # - # y = yaml.load(""" - # - test: success - # """) - # print(y[0]['test']) - # '' - writePython3 = makePythonWriter pkgs.python3 pkgs.python3Packages buildPackages.python3Packages; - - # writePython3Bin takes the same arguments as writePython3 but outputs a directory (like writeScriptBin) - writePython3Bin = name: - writePython3 "/bin/${name}"; - - # writePyPy3 takes a name an attributeset with libraries and some pypy3 sourcecode and - # returns an executable - # - # Example: - # writePyPy3 "test_pypy3" { libraries = [ pkgs.pypy3Packages.pyyaml ]; } '' - # import yaml - # - # y = yaml.load(""" - # - test: success - # """) - # print(y[0]['test']) - # '' - writePyPy3 = makePythonWriter pkgs.pypy3 pkgs.pypy3Packages buildPackages.pypy3Packages; - - # writePyPy3Bin takes the same arguments as writePyPy3 but outputs a directory (like writeScriptBin) - writePyPy3Bin = name: - writePyPy3 "/bin/${name}"; - - - makeFSharpWriter = { dotnet-sdk ? pkgs.dotnet-sdk, fsi-flags ? "", libraries ? _: [] }: nameOrPath: - let - fname = last (builtins.split "/" nameOrPath); - path = if strings.hasSuffix ".fsx" nameOrPath then nameOrPath else "${nameOrPath}.fsx"; - _nugetDeps = mkNugetDeps { name = "${fname}-nuget-deps"; nugetDeps = libraries; }; - - nuget-source = mkNugetSource { - name = "${fname}-nuget-source"; - description = "A Nuget source with the dependencies for ${fname}"; - deps = [ _nugetDeps ]; - }; - - fsi = writeBash "fsi" '' - export HOME=$NIX_BUILD_TOP/.home - export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 - export DOTNET_CLI_TELEMETRY_OPTOUT=1 - export DOTNET_NOLOGO=1 - script="$1"; shift - ${dotnet-sdk}/bin/dotnet fsi --quiet --nologo --readline- ${fsi-flags} "$@" < "$script" - ''; - - in content: writers.makeScriptWriter { - interpreter = fsi; - } path - '' - #i "nuget: ${nuget-source}/lib" - ${ content } - exit 0 - ''; - - writeFSharp = - makeFSharpWriter {}; - - writeFSharpBin = name: - writeFSharp "/bin/${name}"; - -}; + writers = scriptWriters; in writers // (aliases writers) diff --git a/pkgs/build-support/writers/scripts.nix b/pkgs/build-support/writers/scripts.nix new file mode 100644 index 000000000000..b8c5964c3fa3 --- /dev/null +++ b/pkgs/build-support/writers/scripts.nix @@ -0,0 +1,383 @@ +{ pkgs, lib }: +let + inherit (lib) + concatMapStringsSep + elem + escapeShellArg + last + optionalString + stringLength + strings + types + ; + + inherit (pkgs) + buildPackages + gixy + libiconv + mkNugetDeps + mkNugetSource + stdenv + ; +in +rec { + # Base implementation for non-compiled executables. + # Takes an interpreter, for example `${pkgs.bash}/bin/bash` + # + # Examples: + # writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; } + # makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world" + makeScriptWriter = { interpreter, check ? "" }: 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 + name = last (builtins.split "/" nameOrPath); + in + + pkgs.runCommandLocal 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 + + # 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 + ${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: + 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 + name = last (builtins.split "/" nameOrPath); + in + pkgs.runCommand name ((if (types.str.check content) then { + inherit content; + passAsFile = [ "content" ]; + } else { + contentPath = content; + }) // lib.optionalAttrs (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isAarch64) { + # post-link-hook expects codesign_allocate to be in PATH + # https://github.com/NixOS/nixpkgs/issues/154203 + # https://github.com/NixOS/nixpkgs/issues/148189 + nativeBuildInputs = [ stdenv.cc.bintools ]; + }) '' + ${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} + ''} + ''; + + # Like writeScript but the first line is a shebang to bash + # + # Example: + # writeBash "example" '' + # echo hello world + # '' + writeBash = makeScriptWriter { + interpreter = "${pkgs.bash}/bin/bash"; + }; + + # Like writeScriptBin but the first line is a shebang to bash + writeBashBin = name: + writeBash "/bin/${name}"; + + # Like writeScript but the first line is a shebang to dash + # + # Example: + # writeDash "example" '' + # echo hello world + # '' + writeDash = makeScriptWriter { + interpreter = "${pkgs.dash}/bin/dash"; + }; + + # Like writeScriptBin but the first line is a shebang to dash + writeDashBin = name: + writeDash "/bin/${name}"; + + # Like writeScript but the first line is a shebang to fish + # + # Example: + # writeFish "example" '' + # echo hello world + # '' + writeFish = makeScriptWriter { + interpreter = "${pkgs.fish}/bin/fish --no-config"; + check = "${pkgs.fish}/bin/fish --no-config --no-execute"; # syntax check only + }; + + # Like writeScriptBin but the first line is a shebang to fish + writeFishBin = name: + writeFish "/bin/${name}"; + + # writeHaskell takes a name, an attrset with libraries and haskell version (both optional) + # and some haskell source code and returns an executable. + # + # Example: + # writeHaskell "missiles" { libraries = [ pkgs.haskellPackages.acme-missiles ]; } '' + # import Acme.Missiles + # + # main = launchMissiles + # ''; + writeHaskell = name: { + libraries ? [], + ghc ? pkgs.ghc, + ghcArgs ? [], + threadedRuntime ? true, + strip ? true + }: + let + appendIfNotSet = el: list: if elem el list then list else list ++ [ el ]; + ghcArgs' = if threadedRuntime then appendIfNotSet "-threaded" ghcArgs else ghcArgs; + + in makeBinWriter { + compileScript = '' + cp $contentPath tmp.hs + ${ghc.withPackages (_: libraries )}/bin/ghc ${lib.escapeShellArgs ghcArgs'} tmp.hs + mv tmp $out + ''; + inherit strip; + } name; + + # writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin) + writeHaskellBin = name: + writeHaskell "/bin/${name}"; + + writeRust = name: { + rustc ? pkgs.rustc, + rustcArgs ? [], + strip ? true + }: + let + darwinArgs = lib.optionals stdenv.isDarwin [ "-L${lib.getLib libiconv}/lib" ]; + in + makeBinWriter { + compileScript = '' + cp "$contentPath" tmp.rs + PATH=${lib.makeBinPath [pkgs.gcc]} ${lib.getBin rustc}/bin/rustc ${lib.escapeShellArgs rustcArgs} ${lib.escapeShellArgs darwinArgs} -o "$out" tmp.rs + ''; + inherit strip; + } name; + + writeRustBin = name: + writeRust "/bin/${name}"; + + # writeJS takes a name an attributeset with libraries and some JavaScript sourcecode and + # returns an executable + # + # Example: + # writeJS "example" { libraries = [ pkgs.nodePackages.uglify-js ]; } '' + # var UglifyJS = require("uglify-js"); + # var code = "function add(first, second) { return first + second; }"; + # var result = UglifyJS.minify(code); + # console.log(result.code); + # '' + writeJS = name: { libraries ? [] }: content: + let + node-env = pkgs.buildEnv { + name = "node"; + paths = libraries; + pathsToLink = [ + "/lib/node_modules" + ]; + }; + in writeDash name '' + export NODE_PATH=${node-env}/lib/node_modules + exec ${pkgs.nodejs}/bin/node ${pkgs.writeText "js" content} "$@" + ''; + + # writeJSBin takes the same arguments as writeJS but outputs a directory (like writeScriptBin) + writeJSBin = name: + writeJS "/bin/${name}"; + + awkFormatNginx = builtins.toFile "awkFormat-nginx.awk" '' + awk -f + {sub(/^[ \t]+/,"");idx=0} + /\{/{ctx++;idx=1} + /\}/{ctx--} + {id="";for(i=idx;i $out + gixy $out + ''; + + # writePerl takes a name an attributeset with libraries and some perl sourcecode and + # returns an executable + # + # Example: + # writePerl "example" { libraries = [ pkgs.perlPackages.boolean ]; } '' + # use boolean; + # print "Howdy!\n" if true; + # '' + writePerl = name: { libraries ? [] }: + makeScriptWriter { + interpreter = "${pkgs.perl.withPackages (p: libraries)}/bin/perl"; + } name; + + # writePerlBin takes the same arguments as writePerl but outputs a directory (like writeScriptBin) + writePerlBin = name: + writePerl "/bin/${name}"; + + # 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 ? [] }: + let + ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}"; + in + makeScriptWriter { + interpreter = + if libraries == [] + then python.interpreter + else (python.withPackages (ps: libraries)).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 + # + # Example: + # writePyPy2 "test_pypy2" { libraries = [ pkgs.pypy2Packages.enum ]; } '' + # from enum import Enum + # + # class Test(Enum): + # a = "success" + # + # print Test.a + # '' + writePyPy2 = makePythonWriter pkgs.pypy2 pkgs.pypy2Packages buildPackages.pypy2Packages; + + # writePyPy2Bin takes the same arguments as writePyPy2 but outputs a directory (like writeScriptBin) + writePyPy2Bin = name: + writePyPy2 "/bin/${name}"; + + # writePython3 takes a name an attributeset with libraries and some python3 sourcecode and + # returns an executable + # + # Example: + # writePython3 "test_python3" { libraries = [ pkgs.python3Packages.pyyaml ]; } '' + # import yaml + # + # y = yaml.load(""" + # - test: success + # """) + # print(y[0]['test']) + # '' + writePython3 = makePythonWriter pkgs.python3 pkgs.python3Packages buildPackages.python3Packages; + + # writePython3Bin takes the same arguments as writePython3 but outputs a directory (like writeScriptBin) + writePython3Bin = name: + writePython3 "/bin/${name}"; + + # writePyPy3 takes a name an attributeset with libraries and some pypy3 sourcecode and + # returns an executable + # + # Example: + # writePyPy3 "test_pypy3" { libraries = [ pkgs.pypy3Packages.pyyaml ]; } '' + # import yaml + # + # y = yaml.load(""" + # - test: success + # """) + # print(y[0]['test']) + # '' + writePyPy3 = makePythonWriter pkgs.pypy3 pkgs.pypy3Packages buildPackages.pypy3Packages; + + # writePyPy3Bin takes the same arguments as writePyPy3 but outputs a directory (like writeScriptBin) + writePyPy3Bin = name: + writePyPy3 "/bin/${name}"; + + + makeFSharpWriter = { dotnet-sdk ? pkgs.dotnet-sdk, fsi-flags ? "", libraries ? _: [] }: nameOrPath: + let + fname = last (builtins.split "/" nameOrPath); + path = if strings.hasSuffix ".fsx" nameOrPath then nameOrPath else "${nameOrPath}.fsx"; + _nugetDeps = mkNugetDeps { name = "${fname}-nuget-deps"; nugetDeps = libraries; }; + + nuget-source = mkNugetSource { + name = "${fname}-nuget-source"; + description = "A Nuget source with the dependencies for ${fname}"; + deps = [ _nugetDeps ]; + }; + + fsi = writeBash "fsi" '' + export HOME=$NIX_BUILD_TOP/.home + export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 + export DOTNET_CLI_TELEMETRY_OPTOUT=1 + export DOTNET_NOLOGO=1 + script="$1"; shift + ${dotnet-sdk}/bin/dotnet fsi --quiet --nologo --readline- ${fsi-flags} "$@" < "$script" + ''; + + in content: makeScriptWriter { + interpreter = fsi; + } path + '' + #i "nuget: ${nuget-source}/lib" + ${ content } + exit 0 + ''; + + writeFSharp = + makeFSharpWriter {}; + + writeFSharpBin = name: + writeFSharp "/bin/${name}"; + +}