about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJörg Thalheim <Mic92@users.noreply.github.com>2018-11-27 07:17:03 +0000
committerGitHub <noreply@github.com>2018-11-27 07:17:03 +0000
commitf12bd000b9a7f72965c726012fd0d6cca6d4c629 (patch)
treead6b4382a7e6ff499c190a141cbf24f95c432c0d
parent134e0dde366e39bfc21d887208281ffb5c55c2fe (diff)
parent2d02cd7790a2f12227e715800c8fe524de04f196 (diff)
downloadnixlib-f12bd000b9a7f72965c726012fd0d6cca6d4c629.tar
nixlib-f12bd000b9a7f72965c726012fd0d6cca6d4c629.tar.gz
nixlib-f12bd000b9a7f72965c726012fd0d6cca6d4c629.tar.bz2
nixlib-f12bd000b9a7f72965c726012fd0d6cca6d4c629.tar.lz
nixlib-f12bd000b9a7f72965c726012fd0d6cca6d4c629.tar.xz
nixlib-f12bd000b9a7f72965c726012fd0d6cca6d4c629.tar.zst
nixlib-f12bd000b9a7f72965c726012fd0d6cca6d4c629.zip
Merge pull request #49290 from krebs/nix-writers
get nix-writers into nixpkgs
-rw-r--r--pkgs/build-support/writers/default.nix239
-rw-r--r--pkgs/build-support/writers/test.nix149
-rw-r--r--pkgs/test/default.nix2
-rw-r--r--pkgs/top-level/all-packages.nix2
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 000000000000..8aa3e52f5e8b
--- /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 000000000000..68b7b27e6130
--- /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 9315595f8eca..809b2d0b553c 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 80e7f5fbc237..00b1a7c0542d 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