/* 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. */ { analysisJSON, nixpkgs ? import ../Bild.nix {} }: with nixpkgs; let analysis = builtins.fromJSON analysisJSON; build = _: target: let name = target.out; root = builtins.getEnv "BIZ_ROOT"; mainModule = target.mainModule; compileLine = lib.strings.concatStringsSep " " ([target.compiler] ++ target.compilerFlags); allSources = target.srcs ++ [target.quapath]; isEmpty = x: x == null || x == []; skip = ["_" ".direnv"]; filter = file: type: if lib.lists.elem (builtins.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 lib.trivial.pipe file [ (f: lib.strings.removePrefix "${root}/" f) (f: lib.lists.elem f allSources) ] else false; # clean up empty dirs #postUnpack = "find $src -type d -empty -delete"; src = lib.sources.cleanSourceWith {inherit filter; src = lib.sources.cleanSource root;}; langdeps_ = if isEmpty target.langdeps then [] else lib.attrsets.attrVals target.langdeps (lib.attrsets.getAttrFromPath (lib.strings.splitString "." target.packageSet) bild); sysdeps_ = if isEmpty target.sysdeps then [] else lib.attrsets.attrVals target.sysdeps pkgs; BIZ_ROOT = "."; builders = { base = stdenv.mkDerivation rec { inherit name src BIZ_ROOT; buildInputs = langdeps_ ++ sysdeps_; installPhase = "install -D ${name} $out/bin/${name}"; buildPhase = compileLine; }; haskell = stdenv.mkDerivation rec { inherit name src BIZ_ROOT; buildInputs = sysdeps_ ++ [ (bild.haskell.ghcWith (p: (lib.attrsets.attrVals target.langdeps p) )) ]; installPhase = "install -D ${name} $out/bin/${name}"; buildPhase = compileLine; }; c = stdenv.mkDerivation rec { inherit name src BIZ_ROOT; buildInputs = langdeps_ ++ sysdeps_; installPhase = "install -D ${name} $out/bin/${name}"; buildPhase = lib.strings.concatStringsSep " " [ compileLine (if isEmpty langdeps_ then "" else "$(pkg-config --cflags ${lib.strings.concatStringsSep " " target.langdeps})") (if isEmpty sysdeps_ then "" else "$(pkg-config --libs ${lib.strings.concatStringsSep " " target.sysdeps})") ]; }; python = bild.python.buildPythonApplication rec { inherit name src BIZ_ROOT; propagatedBuildInputs = [ (bild.python.pythonWith (_: langdeps_)) ] ++ sysdeps_; buildInputs = sysdeps_; checkInputs = [(bild.python.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 ${mainModule} 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} = ${mainModule}: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 = [mainModule]; # sanity check }; }; in builders.${target.builder}; # the caller gives us the Analysis type, which is a hashmap, but i need to # return a single drv, so just take the first one for now. ideally i would only # pass Target, one at a time, (perhaps parallelized in haskell land) and then i # wouldn't need all of this let nesting in builtins.head (lib.attrsets.mapAttrsToList build analysis)