summaryrefslogtreecommitdiff
path: root/Biz/Bild/Builder.nix
blob: 2b62b89b26d7172414b31e12c57ddec1f962c179 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/*
This is the library of nix builders. Some rules to follow:
- Keep this code as minimal as possible. I'd rather write Haskell than Nix,
  wouldn't you?
- Try to reuse as much upstream Nix as possible.
- Path-like args such as 'srcs' should always be absolute paths.
*/
{ srcs ? "" # list of all source files, as a space-separated string
, main # the entrypoint or main module (not a path)
, root # path to git root
, packageSet # name mapped to private.${packageSet}, e.g. 'ghcWith'
, langdeps ? null # list of deps (as a string), split and passed to packageSet
, sysdeps ? null
, name # exe name
, compileLine ? "" # Target.compiler <> Target.compilerFlags
}:
with import (/. + root + "/Biz/Bild.nix") {};
with builtins;
let
  srcs_ = (lib.strings.splitString " " srcs) ++ [main];

  isEmpty = x: x == null || x == [];

  skip = ["_" ".direnv"];
  filter = file: type:
    if elem (baseNameOf file) skip then false
    # TODO: this means any new directory will cause a rebuild. this bad.  i
    # should recurse into the directory and match against the srcs. for now I
    # just use postUnpack to delete empty dirs
    else if type == "directory" then true
    else if type == "regular" then (builtins.elem file srcs_)
    else false;

  # clean up empty dirs
  postUnpack = "find . -type d -empty -delete";

  src = lib.sources.cleanSourceWith {inherit filter; src = lib.sources.cleanSource root;};

  langdeps_ =
    if isEmpty langdeps then
      []
    else
      private.selectAttrs (lib.strings.splitString " " langdeps) private.${packageSet};
  sysdeps_ =
    if isEmpty sysdeps then
      []
    else
      private.selectAttrs (lib.strings.splitString " " sysdeps) private.nixpkgs.pkgs;
  BIZ_ROOT = ".";
in {
  base = stdenv.mkDerivation rec {
    inherit name src BIZ_ROOT postUnpack;
    buildInputs = langdeps_ ++ sysdeps_;
    installPhase = "install -D ${name} $out/bin/${name}";
    buildPhase = compileLine;
  };

  haskell = stdenv.mkDerivation rec {
    inherit name src BIZ_ROOT postUnpack;
    buildInputs = sysdeps_ ++ [
      (private.ghcWith (p:
        (private.selectAttrs (lib.strings.splitString " " langdeps) p)
      ))
    ];
    installPhase = "install -D ${name} $out/bin/${name}";
    buildPhase = compileLine;
  };

  c = stdenv.mkDerivation rec {
    inherit name src BIZ_ROOT postUnpack;
    buildInputs = langdeps_ ++ sysdeps_;
    installPhase = "install -D ${name} $out/bin/${name}";
    buildPhase = lib.strings.concatStringsSep " " [
      compileLine
      (if isEmpty langdeps then "" else
      "$(pkg-config --cflags ${langdeps})")
      (if isEmpty sysdeps then "" else
      "$(pkg-config --libs ${sysdeps})")
    ];
  };

  python = buildPythonApplication rec {
    inherit name src BIZ_ROOT postUnpack;
    propagatedBuildInputs = [ (private.pythonWith (_: langdeps_)) ] ++ sysdeps_;
    buildInputs = sysdeps_;
    checkInputs = [(private.pythonWith (p: with p; [black mypy pylint]))];
    checkPhase = ''
      check() {
        $@ || { echo "fail:  $name:  $3"; exit 1; }
      }
      check python -m black --quiet --exclude 'setup\.py$' --check .
      check python -m pylint --errors-only .
      check python -m mypy --strict --no-error-summary --exclude 'setup\.py$' .
      check python -m ${main} test
    '';
    preBuild = ''
      # initialize possibly-empty subdirectories as python modules
      find . -type d -exec touch {}/__init__.py \;
      # generate a minimal setup.py
      cat > setup.py << EOF
      from setuptools import setup, find_packages
      setup(
          name='${name}',
          entry_points={'console_scripts':['${name} = ${main}:main']},
          version='0.0.0',
          url='git://simatime.com/biz.git',
          author='dev',
          author_email='dev@simatime.com',
          description='nil',
          packages=find_packages(),
          install_requires=[],
      )
      EOF
    '';
    pythonImportsCheck = [main]; # sanity check
  };
}