diff options
-rw-r--r-- | pkgs/build-support/writers/default.nix | 239 | ||||
-rw-r--r-- | pkgs/build-support/writers/test.nix | 149 | ||||
-rw-r--r-- | pkgs/test/default.nix | 2 | ||||
-rw-r--r-- | pkgs/top-level/all-packages.nix | 2 |
4 files changed, 392 insertions, 0 deletions
diff --git a/pkgs/build-support/writers/default.nix b/pkgs/build-support/writers/default.nix new file mode 100644 index 0000000000000..8aa3e52f5e8bb --- /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 <ncurses.h> + # 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/build-support/writers/test.nix b/pkgs/build-support/writers/test.nix new file mode 100644 index 0000000000000..68b7b27e61309 --- /dev/null +++ b/pkgs/build-support/writers/test.nix @@ -0,0 +1,149 @@ +{ stdenv, lib, runCommand, haskellPackages, nodePackages, perlPackages, python2Packages, python3Packages, writers}: +with writers; +let + + bin = { + bash = writeBashBin "test_writers" '' + if [[ "test" == "test" ]]; then echo "success"; fi + ''; + + c = writeCBin "test_writers" { libraries = [ ]; } '' + #include <stdio.h> + int main() { + printf("success\n"); + return 0; + } + ''; + + dash = writeDashBin "test_writers" '' + test '~' = '~' && echo 'success' + ''; + + haskell = writeHaskellBin "test_writers" { libraries = [ haskellPackages.acme-default ]; } '' + import Data.Default + + int :: Int + int = def + + main :: IO () + main = case int of + 18871 -> putStrLn $ id "success" + _ -> print "fail" + ''; + + js = writeJSBin "test_writers" { libraries = [ nodePackages.semver ]; } '' + var semver = require('semver'); + + if (semver.valid('1.2.3')) { + console.log('success') + } else { + console.log('fail') + } + ''; + + perl = writePerlBin "test_writers" { libraries = [ perlPackages.boolean ]; } '' + use boolean; + print "success\n" if true; + ''; + + python2 = writePython2Bin "test_writers" { libraries = [ python2Packages.enum ]; } '' + from enum import Enum + + class Test(Enum): + a = "success" + + print Test.a + ''; + + python3 = writePython3Bin "test_writers" { libraries = [ python3Packages.pyyaml ]; } '' + import yaml + + y = yaml.load(""" + - test: success + """) + print(y[0]['test']) + ''; + }; + + simple = { + bash = writeBash "test_bash" '' + if [[ "test" == "test" ]]; then echo "success"; fi + ''; + + c = writeC "test_c" { libraries = [ ]; } '' + #include <stdio.h> + int main() { + printf("success\n"); + return 0; + } + ''; + + dash = writeDash "test_dash" '' + test '~' = '~' && echo 'success' + ''; + + haskell = writeHaskell "test_haskell" { libraries = [ haskellPackages.acme-default ]; } '' + import Data.Default + + int :: Int + int = def + + main :: IO () + main = case int of + 18871 -> putStrLn $ id "success" + _ -> print "fail" + ''; + + js = writeJS "test_js" { libraries = [ nodePackages.semver ]; } '' + var semver = require('semver'); + + if (semver.valid('1.2.3')) { + console.log('success') + } else { + console.log('fail') + } + ''; + + perl = writePerl "test_perl" { libraries = [ perlPackages.boolean ]; } '' + use boolean; + print "success\n" if true; + ''; + + python2 = writePython2 "test_python2" { libraries = [ python2Packages.enum ]; } '' + from enum import Enum + + class Test(Enum): + a = "success" + + print Test.a + ''; + + python3 = writePython3 "test_python3" { libraries = [ python3Packages.pyyaml ]; } '' + import yaml + + y = yaml.load(""" + - test: success + """) + print(y[0]['test']) + ''; + }; + + writeTest = expectedValue: test: + writeDash "test-writers" '' + if test "$(${test})" != "${expectedValue}"; then + echo 'test ${test} failed' + exit 1 + fi + ''; + +in runCommand "test-writers" { + passthru = { inherit writeTest bin simple; }; + meta.platforms = stdenv.lib.platforms.all; +} '' + ${lib.concatMapStringsSep "\n" (test: writeTest "success" "${test}/bin/test_writers") (lib.attrValues bin)} + ${lib.concatMapStringsSep "\n" (test: writeTest "success" "${test}") (lib.attrValues simple)} + + echo 'nix-writers successfully tested' >&2 + touch $out +'' + diff --git a/pkgs/test/default.nix b/pkgs/test/default.nix index 9315595f8eca4..809b2d0b553ca 100644 --- a/pkgs/test/default.nix +++ b/pkgs/test/default.nix @@ -33,4 +33,6 @@ with pkgs; nixos-functions = callPackage ./nixos-functions {}; patch-shebangs = callPackage ./patch-shebangs {}; + + writers = callPackage ../build-support/writers/test.nix {}; } diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 80e7f5fbc237f..00b1a7c0542d1 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -420,6 +420,8 @@ with pkgs; iconConvTools = callPackage ../build-support/icon-conv-tools {}; + #package writers + writers = callPackage ../build-support/writers {}; ### TOOLS |