summary refs log tree commit diff
diff options
context:
space:
mode:
authorzimbatm <zimbatm@zimbatm.com>2023-07-22 13:48:25 +0200
committerzimbatm <zimbatm@zimbatm.com>2023-07-22 17:54:41 +0200
commitd0c7ffc596bd8da9298d693d1bdd0559c6777311 (patch)
tree74548eeab1860ae47c81e7e03d518f3b3e9766ac
parent73ee03cbc5a20c6c3d61946558e7aea4a48c5119 (diff)
downloadnixpkgs-d0c7ffc596bd8da9298d693d1bdd0559c6777311.tar
nixpkgs-d0c7ffc596bd8da9298d693d1bdd0559c6777311.tar.gz
nixpkgs-d0c7ffc596bd8da9298d693d1bdd0559c6777311.tar.bz2
nixpkgs-d0c7ffc596bd8da9298d693d1bdd0559c6777311.tar.lz
nixpkgs-d0c7ffc596bd8da9298d693d1bdd0559c6777311.tar.xz
nixpkgs-d0c7ffc596bd8da9298d693d1bdd0559c6777311.tar.zst
nixpkgs-d0c7ffc596bd8da9298d693d1bdd0559c6777311.zip
writers: make room for other types of writers
-rw-r--r--pkgs/build-support/writers/default.nix364
-rw-r--r--pkgs/build-support/writers/scripts.nix383
2 files changed, 386 insertions, 361 deletions
diff --git a/pkgs/build-support/writers/default.nix b/pkgs/build-support/writers/default.nix
index faac553dee3..c26ada8bc57 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<ctx;i++)id=sprintf("%s%s", id, "\t");printf "%s%s\n", id, $0}
-   '';
-
-  writeNginxConfig = name: text: pkgs.runCommandLocal name {
-    inherit text;
-    passAsFile = [ "text" ];
-    nativeBuildInputs = [ gixy ];
-  } /* sh */ ''
-    # nginx-config-formatter has an error - https://github.com/1connect/nginx-config-formatter/issues/16
-    awk -f ${awkFormatNginx} "$textPath" | sed '/^\s*$/d' > $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 00000000000..b8c5964c3fa
--- /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<ctx;i++)id=sprintf("%s%s", id, "\t");printf "%s%s\n", id, $0}
+   '';
+
+  writeNginxConfig = name: text: pkgs.runCommandLocal name {
+    inherit text;
+    passAsFile = [ "text" ];
+    nativeBuildInputs = [ gixy ];
+  } /* sh */ ''
+    # nginx-config-formatter has an error - https://github.com/1connect/nginx-config-formatter/issues/16
+    awk -f ${awkFormatNginx} "$textPath" | sed '/^\s*$/d' > $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}";
+
+}