/* 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 "CODEROOT"; 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 preBuild 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; # remove empty directories, leftover from the src filter preBuild = "find . -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; rundeps_ = if isEmpty target.rundeps then [] else lib.attrsets.attrVals target.rundeps bild.pkgs; CODEROOT = "."; builders = { base = stdenv.mkDerivation rec { inherit name src CODEROOT preBuild; buildInputs = langdeps_ ++ sysdeps_; installPhase = "install -D ${name} $out/bin/${name}"; buildPhase = compileLine; }; haskell = stdenv.mkDerivation rec { inherit name src CODEROOT preBuild; nativeBuildInputs = [ makeWrapper ]; buildInputs = sysdeps_ ++ [ (bild.haskell.ghcWith (p: (lib.attrsets.attrVals target.langdeps p) )) ]; buildPhase = compileLine; installPhase = '' install -D ${name} $out/bin/${name} wrapProgram $out/bin/${name} \ --prefix PATH : ${lib.makeBinPath rundeps_} ''; }; c = stdenv.mkDerivation rec { inherit name src CODEROOT preBuild; 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 CODEROOT; propagatedBuildInputs = langdeps_ ++ sysdeps_; buildInputs = sysdeps_; nativeCheckInputs = [ black mypy ruff ]; checkPhase = '' check() { $@ || { echo "fail: $name: $3"; exit 1; } } cp ${../../pyproject.toml} ./pyproject.toml check python -m black --quiet --exclude 'setup\.py$' --check . check ${ruff}/bin/ruff check . touch ./py.typed check python -m mypy \ --explicit-package-bases \ --no-error-summary \ --exclude 'setup\.py$' \ . check python -m ${mainModule} test ''; preBuild = '' # remove empty directories, leftover from the src filter find . -type d -empty -delete # initialize remaining dirs 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 bild 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)