From 85a8d2229028eb3d07ec415500be98c212d4b845 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Fri, 9 Aug 2024 14:04:52 +0200 Subject: [PATCH] Pkgs.writers: document {makeScriptWriter,makeScriptWriterBin} --- pkgs/build-support/writers/scripts.nix | 748 +++++++++++++++---------- 1 file changed, 465 insertions(+), 283 deletions(-) diff --git a/pkgs/build-support/writers/scripts.nix b/pkgs/build-support/writers/scripts.nix index bceac1b0c959..2d226056c761 100644 --- a/pkgs/build-support/writers/scripts.nix +++ b/pkgs/build-support/writers/scripts.nix @@ -21,14 +21,82 @@ let ; in rec { - # Base implementation for non-compiled executables. - # Takes an interpreter, for example `${lib.getExe pkgs.bash}` - # - # Examples: - # writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; } - # makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world" - makeScriptWriter = { interpreter, check ? "", makeWrapperArgs ? [], }: nameOrPath: content: - assert (types.path.check nameOrPath) || (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null); + /** + A generic function that returns a derivation which, when beeing built outputs the script in an executable format. + + # Inputs + + config (AttrSet) + : `interpreter` (String) + : the interpreter to use for the script + : `check` (String) + : A command to check the script. I.e. some linting check. + : `makeWrapperArgs` (Optional, [ String ], Default: []) + : Arguments forwarded to (`makeWrapper`)[#fun-makeWrapper] + + `nameOrPath` (String) + : The name of the script or the path to the script. + + When a `string` starting with "/" is passed, the script will be created at the specified path in $out. + I.e. `"/bin/hello"` will create a script at `$out/bin/hello`. + + Any other `string` is interpreted as filename. + It must be a simple unix filename starting with a letter, digit, dot, or underscore. + Spaces or special characters are not allowed. + + `content` (String) + : The content of the script. + + :::{.note} + This function is used as base implementation for other high-level writer functions. + + For example, `writeBash` can (roughly) be implemented as: + ```nix + writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; } + ``` + ::: + + # Examples + :::{.example} + ## `pkgs.writers.makeScriptWriter` dash example + + ```nix-repl + :b makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world" + -> /nix/store/indvlr9ckmnv4f0ynkmasv2h4fxhand0-hello + ``` + + The above example creates a script named `hello` that outputs `hello world` when executed. + + ```sh + > /nix/store/indvlr9ckmnv4f0ynkmasv2h4fxhand0-hello + hello world + ``` + ::: + + :::{.example} + ## `pkgs.writers.makeScriptWriter` python example + + ```nix-repl + :b makeScriptWriter { interpreter = "${pkgs.python3}/bin/python"; } "python-hello" "print('hello world')" + -> /nix/store/4kvby1hqr45ffcdrvfpnpj62hanskw93-python-hello + ``` + + ```sh + > /nix/store/4kvby1hqr45ffcdrvfpnpj62hanskw93-python-hello + hello world + ``` + ::: + */ + makeScriptWriter = + { + interpreter, + check ? "", + makeWrapperArgs ? [ ], + }: + nameOrPath: content: + assert + (types.path.check nameOrPath) + || (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null); assert (types.path.check content) || (types.str.check content); let nameIsPath = types.path.check nameOrPath; @@ -37,83 +105,146 @@ rec { # 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; + 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) - - if isScript $wrapperInterpreter + '' + # 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 - echo "error: passed interpreter ($interpreter) is a script which has another script ($wrapperInterpreter) as an interpreter, which is not supported." - exit 1 + 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 - # 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 - 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 + ''; - # 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 - ''; + /** + This is a generic function that returns a derivation which, when built, compiles the given script into an executable format. + :::{.note} + This function is the base implementation for other compile language `writers`. + i.e. `writeHaskell`, `writeRust`. + ::: - # 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, makeWrapperArgs ? [] }: nameOrPath: content: - assert (types.path.check nameOrPath) || (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null); + # Inputs + + config (AttrSet) + : `compileScript` (String) + : The script that compiles the given content into an executable. + + : `strip` (Boolean, Default: true) + : Whether to strip the executable or not. + + : `makeWrapperArgs` (Optional, [ String ], Default: []) + : Arguments forwarded to (`makeWrapper`)[#fun-makeWrapper] + + `nameOrPath` (String) + : The name of the script or the path to the script. + + When a `string` starting with "/" is passed, the script will be created at the specified path in $out. + I.e. `"/bin/hello"` will create a script at `$out/bin/hello`. + + Any other `string` is interpreted as filename. + It must be a simple unix filename starting with a letter, digit, dot, or underscore. + Spaces or special characters are not allowed. + + # Examples + :::{.example} + ## `pkgs.writers.makeBinWriter` example + + ```c + // main.c + #include + + int main() + { + printf("Hello, World!\n"); + return 0; + } + ``` + + ```nix-repl + :b makeBinWriter { compileScript = "${pkgs.gcc}/bin/gcc -o $out $contentPath"; } "hello" ./main.c + out -> /nix/store/f6crc8mwj3lvcxqclw7n09cm8nb6kxbh-hello + ``` + + The above example creates an executable named `hello` that outputs `Hello, World!` when executed. + + ```sh + > /nix/store/f6crc8mwj3lvcxqclw7n09cm8nb6kxbh-hello + Hello, World! + ``` + ::: + */ + makeBinWriter = + { + compileScript, + strip ? true, + makeWrapperArgs ? [ ], + }: + nameOrPath: content: + assert + (types.path.check nameOrPath) + || (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null); assert (types.path.check content) || (types.str.check content); let nameIsPath = types.path.check nameOrPath; @@ -122,46 +253,46 @@ rec { # 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; - } + 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 - ''; + '' + ${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 - 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 {} '' + 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 ''; @@ -184,10 +315,12 @@ rec { # '' # 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; + 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 # @@ -208,8 +341,7 @@ rec { # '' # hello # '' - writeBashBin = name: - writeBash "/bin/${name}"; + writeBashBin = name: writeBash "/bin/${name}"; # Like writeScript but the first line is a shebang to dash # @@ -230,10 +362,12 @@ rec { # '' # 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; + 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 # @@ -254,8 +388,7 @@ rec { # '' # hello # '' - writeDashBin = name: - writeDash "/bin/${name}"; + writeDashBin = name: writeDash "/bin/${name}"; # Like writeScript but the first line is a shebang to fish # @@ -276,16 +409,21 @@ rec { # '' # 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; + 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 # @@ -306,8 +444,7 @@ rec { # '' # hello # '' - writeFishBin = name: - writeFish "/bin/${name}"; + 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. @@ -318,30 +455,32 @@ rec { # # main = launchMissiles # ''; - writeHaskell = name: { - ghc ? pkgs.ghc, - ghcArgs ? [], - libraries ? [], - makeWrapperArgs ? [], - strip ? true, - threadedRuntime ? true, - }: + writeHaskell = + name: + { + ghc ? pkgs.ghc, + ghcArgs ? [ ], + libraries ? [ ], + makeWrapperArgs ? [ ], + strip ? true, + threadedRuntime ? 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 { + in + makeBinWriter { compileScript = '' cp $contentPath tmp.hs - ${(ghc.withPackages (_: libraries ))}/bin/ghc ${lib.escapeShellArgs ghcArgs'} tmp.hs + ${(ghc.withPackages (_: libraries))}/bin/ghc ${lib.escapeShellArgs ghcArgs'} tmp.hs mv tmp $out ''; inherit makeWrapperArgs strip; } name; # writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin) - writeHaskellBin = name: - writeHaskell "/bin/${name}"; + writeHaskellBin = name: writeHaskell "/bin/${name}"; # Like writeScript but the first line is a shebang to nu # @@ -362,11 +501,14 @@ rec { # '' # 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; - + 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 # @@ -387,25 +529,27 @@ rec { # '' # hello # '' - writeNuBin = name: - writeNu "/bin/${name}"; + 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 ? [], ... } @ 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; + 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 # @@ -415,26 +559,32 @@ rec { # '' writeRuby = makeRubyWriter pkgs.ruby pkgs.rubyPackages buildPackages.rubyPackages; - writeRubyBin = name: - writeRuby "/bin/${name}"; + writeRubyBin = name: writeRuby "/bin/${name}"; # 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 ? [], ... } @ args: - makeScriptWriter ( - (builtins.removeAttrs args ["libraries"]) - // { - interpreter = lua.interpreter; + 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; + 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) @@ -458,28 +608,28 @@ rec { # '' writeLua = makeLuaWriter pkgs.lua pkgs.luaPackages buildPackages.luaPackages; - writeLuaBin = name: - writeLua "/bin/${name}"; + writeLuaBin = name: writeLua "/bin/${name}"; - writeRust = name: { - makeWrapperArgs ? [], - rustc ? pkgs.rustc, - rustcArgs ? [], - strip ? true, - }: - let - darwinArgs = lib.optionals stdenv.isDarwin [ "-L${lib.getLib libiconv}/lib" ]; - in + writeRust = + name: + { + makeWrapperArgs ? [ ], + 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]} ${rustc}/bin/rustc ${lib.escapeShellArgs rustcArgs} ${lib.escapeShellArgs darwinArgs} -o "$out" tmp.rs + PATH=${lib.makeBinPath [ pkgs.gcc ]} ${rustc}/bin/rustc ${lib.escapeShellArgs rustcArgs} ${lib.escapeShellArgs darwinArgs} -o "$out" tmp.rs ''; inherit makeWrapperArgs strip; } name; - writeRustBin = name: - writeRust "/bin/${name}"; + writeRustBin = name: writeRust "/bin/${name}"; # writeJS takes a name an attributeset with libraries and some JavaScript sourcecode and # returns an executable @@ -491,23 +641,26 @@ rec { # 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 ${lib.getExe pkgs.nodejs} ${pkgs.writeText "js" content} "$@" - ''; + 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 ${lib.getExe pkgs.nodejs} ${pkgs.writeText "js" content} "$@" + ''; # writeJSBin takes the same arguments as writeJS but outputs a directory (like writeScriptBin) - writeJSBin = name: - writeJS "/bin/${name}"; + writeJSBin = name: writeJS "/bin/${name}"; awkFormatNginx = builtins.toFile "awkFormat-nginx.awk" '' awk -f @@ -515,18 +668,22 @@ rec { /\{/{ctx++;idx=1} /\}/{ctx--} {id="";for(i=idx;i $out - gixy $out ''; + 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 # @@ -535,42 +692,55 @@ rec { # use boolean; # print "Howdy!\n" if true; # '' - writePerl = name: { libraries ? [], ... } @ args: + writePerl = + name: + { + libraries ? [ ], + ... + }@args: makeScriptWriter ( - (builtins.removeAttrs args ["libraries"]) + (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: - writePerl "/bin/${name}"; + 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 ? [], ... } @ args: - let - ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}"; - in - makeScriptWriter - ( - (builtins.removeAttrs args ["libraries" "flakeIgnore"]) + makePythonWriter = + python: pythonPackages: buildPythonPackages: name: + { + libraries ? [ ], + flakeIgnore ? [ ], + ... + }@args: + let + ignoreAttribute = + optionalString (flakeIgnore != [ ]) + "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}"; + in + 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" - ''); + 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; + ) name; # writePyPy2 takes a name an attributeset with libraries and some pypy2 sourcecode and # returns an executable @@ -587,8 +757,7 @@ rec { 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}"; + writePyPy2Bin = name: writePyPy2 "/bin/${name}"; # writePython3 takes a name an attributeset with libraries and some python3 sourcecode and # returns an executable @@ -605,8 +774,7 @@ rec { 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}"; + writePython3Bin = name: writePython3 "/bin/${name}"; # writePyPy3 takes a name an attributeset with libraries and some pypy3 sourcecode and # returns an executable @@ -623,46 +791,60 @@ rec { 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}"; + writePyPy3Bin = name: writePyPy3 "/bin/${name}"; + 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"; + _nugetDeps = mkNugetDeps { + name = "${fname}-nuget-deps"; + nugetDeps = libraries; + }; - 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"; - _nugetDeps = mkNugetDeps { name = "${fname}-nuget-deps"; nugetDeps = libraries; }; + nuget-source = mkNugetSource { + name = "${fname}-nuget-source"; + description = "Nuget source with the dependencies for ${fname}"; + deps = [ _nugetDeps ]; + }; - nuget-source = mkNugetSource { - name = "${fname}-nuget-source"; - description = "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 + ${lib.getExe dotnet-sdk} fsi --quiet --nologo --readline- ${fsi-flags} "$@" < "$script" + ''; - 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 - ${lib.getExe dotnet-sdk} fsi --quiet --nologo --readline- ${fsi-flags} "$@" < "$script" - ''; + in + content: + makeScriptWriter + ( + (builtins.removeAttrs args [ + "dotnet-sdk" + "fsi-flags" + "libraries" + ]) + // { + interpreter = fsi; + } + ) + path + '' + #i "nuget: ${nuget-source}/lib" + ${content} + exit 0 + ''; - in content: makeScriptWriter ( - (builtins.removeAttrs args ["dotnet-sdk" "fsi-flags" "libraries"]) - // { - interpreter = fsi; - } - ) path - '' - #i "nuget: ${nuget-source}/lib" - ${ content } - exit 0 - ''; + writeFSharp = makeFSharpWriter { }; - writeFSharp = - makeFSharpWriter {}; - - writeFSharpBin = name: - writeFSharp "/bin/${name}"; + writeFSharpBin = name: writeFSharp "/bin/${name}"; }