summaryrefslogtreecommitdiff
path: root/Omni/Bild/Builder.nix
diff options
context:
space:
mode:
Diffstat (limited to 'Omni/Bild/Builder.nix')
-rw-r--r--Omni/Bild/Builder.nix168
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)