diff options
Diffstat (limited to 'Omni/Bild/Builder.nix')
-rw-r--r-- | Omni/Bild/Builder.nix | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/Omni/Bild/Builder.nix b/Omni/Bild/Builder.nix new file mode 100644 index 0000000..a78f311 --- /dev/null +++ b/Omni/Bild/Builder.nix @@ -0,0 +1,168 @@ +/* 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 . + check ruff check --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) |