diff --git a/pkgs/build-support/writers/default.nix b/pkgs/build-support/writers/default.nix new file mode 100644 index 00000000000..8aa3e52f5e8 --- /dev/null +++ b/pkgs/build-support/writers/default.nix @@ -0,0 +1,239 @@ +{ pkgs, lib }: + +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 ? "" }: name: text: + assert lib.or (types.path.check name) (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" name != null); + + pkgs.writeTextFile { + name = last (builtins.split "/" name); + executable = true; + destination = if types.path.check name then name else ""; + text = '' + #! ${interpreter} + ${text} + ''; + checkPhase = check; + }; + + # 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}"; + + # writeC writes an executable c package called `name` to `destination` using `libraries`. + # + # Examples: + # writeC "hello-world-ncurses" { libraries = [ pkgs.ncurses ]; } '' + # #include + # int main() { + # initscr(); + # printw("Hello World !!!"); + # refresh(); endwin(); + # return 0; + # } + # '' + writeC = name: { + libraries ? [], + }: text: pkgs.runCommand name { + inherit text; + buildInputs = [ pkgs.pkgconfig ] ++ libraries; + passAsFile = [ "text" ]; + } '' + PATH=${makeBinPath [ + pkgs.binutils-unwrapped + pkgs.coreutils + pkgs.gcc + pkgs.pkgconfig + ]} + mkdir -p "$(dirname "$out")" + gcc \ + ${optionalString (libraries != []) + "$(pkg-config --cflags --libs ${ + concatMapStringsSep " " (lib: escapeShellArg (builtins.parseDrvName lib.name).name) (libraries) + })" + } \ + -O \ + -o "$out" \ + -Wall \ + -x c \ + "$textPath" + strip --strip-unneeded "$out" + ''; + + # writeCBin takes the same arguments as writeC but outputs a directory (like writeScriptBin) + writeCBin = name: spec: text: + pkgs.runCommand name { + } '' + mkdir -p $out/bin + ln -s ${writeC name spec text} $out/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}"; + + # 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 + }: text: pkgs.runCommand name { + inherit text; + passAsFile = [ "text" ]; + } '' + cp $textPath ${name}.hs + ${ghc.withPackages (_: libraries )}/bin/ghc ${name}.hs + cp ${name} $out + ''; + + # writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin) + writeHaskellBin = name: spec: text: + pkgs.runCommand name { + } '' + mkdir -p $out/bin + ln -s ${writeHaskell name spec text} $out/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 ? [] }: text: + 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" text} + ''; + + # writeJSBin takes the same arguments as writeJS but outputs a directory (like writeScriptBin) + writeJSBin = name: + writeJS "/bin/${name}"; + + # 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 ? [] }: + let + perl-env = pkgs.buildEnv { + name = "perl-environment"; + paths = libraries; + pathsToLink = [ + "/lib/perl5/site_perl" + ]; + }; + in + makeScriptWriter { + interpreter = "${pkgs.perl}/bin/perl -I ${perl-env}/lib/perl5/site_perl"; + } name; + + # writePerlBin takes the same arguments as writePerl but outputs a directory (like writeScriptBin) + writePerlBin = name: + writePerl "/bin/${name}"; + + # writePython2 takes a name an attributeset with libraries and some python2 sourcecode and + # returns an executable + # + # Example: + # writePython2 "test_python2" { libraries = [ pkgs.python2Packages.enum ]; } '' + # from enum import Enum + # + # class Test(Enum): + # a = "success" + # + # print Test.a + # '' + writePython2 = name: { libraries ? [], flakeIgnore ? [] }: + let + py = pkgs.python2.withPackages (ps: libraries); + ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}"; + in + makeScriptWriter { + interpreter = "${py}/bin/python"; + check = writeDash "python2check.sh" '' + exec ${pkgs.python2Packages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1" + ''; + } name; + + # writePython2Bin takes the same arguments as writePython2 but outputs a directory (like writeScriptBin) + writePython2Bin = name: + writePython2 "/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 = name: { libraries ? [], flakeIgnore ? [] }: + let + py = pkgs.python3.withPackages (ps: libraries); + ignoreAttribute = optionalString (flakeIgnore != []) "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}"; + in + makeScriptWriter { + interpreter = "${py}/bin/python"; + check = writeDash "python3check.sh" '' + exec ${pkgs.python3Packages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1" + ''; + } name; + + # writePython3Bin takes the same arguments as writePython3 but outputs a directory (like writeScriptBin) + writePython3Bin = name: + writePython3 "/bin/${name}"; +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 782fb6d8dd8..656faf50610 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -408,6 +408,8 @@ with pkgs; iconConvTools = callPackage ../build-support/icon-conv-tools {}; + #package writers + writers = callPackage ../build-support/writers {}; ### TOOLS