/* 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, bild }: with bild; let analysis = builtins.fromJSON analysisJSON; # common bash functions for the builder commonBash = builtins.toFile "common.bash" '' # Check that a command succeeds, fail and log if not. function check { $@ || { echo "fail: $name: $3"; exit 1; } } ''; 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 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_ ++ [ (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 = python.buildPythonApplication rec { inherit name src CODEROOT; nativeBuildInputs = [ makeWrapper ]; propagatedBuildInputs = langdeps_ ++ sysdeps_ ++ rundeps_; buildInputs = sysdeps_; nativeCheckInputs = [ pkgs.ruff python.packages.mypy ]; checkPhase = '' . ${commonBash} cp ${../../pyproject.toml} ./pyproject.toml check ruff format --exclude 'setup.py' --check . # ignore EXE here to support run.sh shebangs check ruff check \ --ignore EXE \ --exclude 'setup.py' \ --exclude '__init__.py' \ . touch ./py.typed check python -m mypy \ --explicit-package-bases \ --no-error-summary \ --exclude 'setup\.py$' \ . ''; installCheck = '' . ${commonBash} 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 find_packages, setup setup( name="${name}", entry_points={"console_scripts":["${name} = ${mainModule}:main"]}, version="0.0.0", url="git://simatime.com/omni.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)