From e5a6175e044d69b8f598a2c2acb9bcfd77b9001c Mon Sep 17 00:00:00 2001 From: Ben Sima Date: Mon, 21 Aug 2023 20:36:12 -0400 Subject: Refactor the build system for readability Lots of changes here but the code is much improved. The nix code is clearer and structured better. The Haskell code improved in response to the nix changes. I needed to use a qualified path instead of the abspath because the BIZ_ROOT changes based on whether bild runs in nix or runs in the user environment. Rather than passing every argument into Builder.nix, now I just pass the json from bild and deconstruct it in nix. This is obviously a much better design and it only came to be after sleeping on it the other night. --- Biz/Bild/Builder.nix | 214 ++++++++++++++++++++++++---------------------- Biz/Bild/Deps/C.nix | 3 + Biz/Bild/Deps/Haskell.nix | 144 +++++++++++++++---------------- Biz/Bild/Sources.json | 12 --- 4 files changed, 186 insertions(+), 187 deletions(-) create mode 100644 Biz/Bild/Deps/C.nix (limited to 'Biz/Bild') diff --git a/Biz/Bild/Builder.nix b/Biz/Bild/Builder.nix index 2b62b89..a5a31c7 100644 --- a/Biz/Bild/Builder.nix +++ b/Biz/Bild/Builder.nix @@ -3,115 +3,125 @@ 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. -- Path-like args such as 'srcs' should always be absolute paths. */ -{ srcs ? "" # list of all source files, as a space-separated string -, main # the entrypoint or main module (not a path) -, root # path to git root -, packageSet # name mapped to private.${packageSet}, e.g. 'ghcWith' -, langdeps ? null # list of deps (as a string), split and passed to packageSet -, sysdeps ? null -, name # exe name -, compileLine ? "" # Target.compiler <> Target.compilerFlags -}: -with import (/. + root + "/Biz/Bild.nix") {}; -with builtins; +{ analysisJSON, nixpkgs ? import ../Bild.nix {} }: +with nixpkgs; let - srcs_ = (lib.strings.splitString " " srcs) ++ [main]; + 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); - isEmpty = x: x == null || x == []; + allSources = target.srcs ++ [target.quapath]; - skip = ["_" ".direnv"]; - filter = file: type: - if elem (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 (builtins.elem file srcs_) - else false; + isEmpty = x: x == null || x == []; - # clean up empty dirs - postUnpack = "find . -type d -empty -delete"; + 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; - src = lib.sources.cleanSourceWith {inherit filter; src = lib.sources.cleanSource root;}; + # clean up empty dirs + #postUnpack = "find $src -type d -empty -delete"; - langdeps_ = - if isEmpty langdeps then - [] - else - private.selectAttrs (lib.strings.splitString " " langdeps) private.${packageSet}; - sysdeps_ = - if isEmpty sysdeps then - [] - else - private.selectAttrs (lib.strings.splitString " " sysdeps) private.nixpkgs.pkgs; - BIZ_ROOT = "."; -in { - base = stdenv.mkDerivation rec { - inherit name src BIZ_ROOT postUnpack; - buildInputs = langdeps_ ++ sysdeps_; - installPhase = "install -D ${name} $out/bin/${name}"; - buildPhase = compileLine; - }; + src = lib.sources.cleanSourceWith {inherit filter; src = lib.sources.cleanSource root;}; - haskell = stdenv.mkDerivation rec { - inherit name src BIZ_ROOT postUnpack; - buildInputs = sysdeps_ ++ [ - (private.ghcWith (p: - (private.selectAttrs (lib.strings.splitString " " langdeps) p) - )) - ]; - installPhase = "install -D ${name} $out/bin/${name}"; - buildPhase = compileLine; - }; + 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 = "."; - c = stdenv.mkDerivation rec { - inherit name src BIZ_ROOT postUnpack; - buildInputs = langdeps_ ++ sysdeps_; - installPhase = "install -D ${name} $out/bin/${name}"; - buildPhase = lib.strings.concatStringsSep " " [ - compileLine - (if isEmpty langdeps then "" else - "$(pkg-config --cflags ${langdeps})") - (if isEmpty sysdeps then "" else - "$(pkg-config --libs ${sysdeps})") - ]; - }; + builders = { + base = stdenv.mkDerivation rec { + inherit name src BIZ_ROOT; + buildInputs = langdeps_ ++ sysdeps_; + installPhase = "install -D ${name} $out/bin/${name}"; + buildPhase = compileLine; + }; - python = buildPythonApplication rec { - inherit name src BIZ_ROOT postUnpack; - propagatedBuildInputs = [ (private.pythonWith (_: langdeps_)) ] ++ sysdeps_; - buildInputs = sysdeps_; - checkInputs = [(private.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 ${main} 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} = ${main}: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 = [main]; # sanity check - }; -} + 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) diff --git a/Biz/Bild/Deps/C.nix b/Biz/Bild/Deps/C.nix new file mode 100644 index 0000000..45cae1b --- /dev/null +++ b/Biz/Bild/Deps/C.nix @@ -0,0 +1,3 @@ +[ + "libsodium" +] diff --git a/Biz/Bild/Deps/Haskell.nix b/Biz/Bild/Deps/Haskell.nix index 066fd86..f34bfab 100644 --- a/Biz/Bild/Deps/Haskell.nix +++ b/Biz/Bild/Deps/Haskell.nix @@ -1,76 +1,74 @@ -hpkgs: +# This is the global set of Haskell packages which gets deployed to Hoogle, and +# is available for selecting. -# This is the global set of Haskell packages which gets deployed to Hoogle. - -with hpkgs; [ - MonadRandom - QuickCheck - SafeSemaphore - acid-state - aeson - async - bytestring - clay - cmark - cmark-lucid - conduit - conduit-extra - config-ini - containers - directory - docopt - envy - fast-logger - filepath - github - hashids - haskeline - hmacaroons - hostname - http-types - ixset - katip - lucid - monad-logger - monad-metrics - mtl - neat-interpolation - network-uri - niv - optparse-simple - parsec - process - protolude - quickcheck-instances - rainbow - random - regex-applicative - req - safecopy - servant - servant-auth - servant-auth-server - servant-lucid - servant-server - split - stm - stripe-haskell - tasty - tasty-hunit - tasty-quickcheck - text - time - transformers - unagi-chan - unix - unordered-containers - uuid - vector - wai - wai-app-static - wai-extra - wai-middleware-metrics - warp - x509 + "MonadRandom" + "QuickCheck" + "SafeSemaphore" + "acid-state" + "aeson" + "async" + "base" + "bytestring" + "clay" + "cmark" + "cmark-lucid" + "conduit" + "conduit-extra" + "config-ini" + "containers" + "directory" + "docopt" + "envy" + "fast-logger" + "filepath" + "github" + "hashids" + "haskeline" + "hostname" + "http-types" + "ixset" + "katip" + "lucid" + "monad-logger" + "monad-metrics" + "mtl" + "neat-interpolation" + "network-uri" + "niv" + "optparse-simple" + "parsec" + "process" + "protolude" + "quickcheck-instances" + "rainbow" + "random" + "regex-applicative" + "req" + "safecopy" + "servant" + "servant-auth" + "servant-auth-server" + "servant-lucid" + "servant-server" + "split" + "stm" + "stripe-haskell" + "tasty" + "tasty-hunit" + "tasty-quickcheck" + "text" + "time" + "transformers" + "unagi-chan" + "unix" + "unordered-containers" + "uuid" + "vector" + "wai" + "wai-app-static" + "wai-extra" + "wai-middleware-metrics" + "warp" + "x509" ] diff --git a/Biz/Bild/Sources.json b/Biz/Bild/Sources.json index 5d05ea0..e4fcfd4 100644 --- a/Biz/Bild/Sources.json +++ b/Biz/Bild/Sources.json @@ -56,18 +56,6 @@ "type": "git", "version": "2021-06-14-unstable" }, - "hmacaroons": { - "branch": "master", - "description": "Pure haskell implementation of macaroons", - "homepage": "https://jtanguy.github.io/hmacaroons", - "owner": "jtanguy", - "repo": "hmacaroons", - "rev": "6fbca87836a4baef171c5ffc774387766c709fbf", - "sha256": "0qd1ifx1rzcv8rc74vb5xxgi544qxclx8ky3wjg0nbj22hpvvg6j", - "type": "tarball", - "url": "https://github.com/jtanguy/hmacaroons/archive/6fbca87836a4baef171c5ffc774387766c709fbf.tar.gz", - "url_template": "https://github.com///archive/.tar.gz" - }, "inspekt3d": { "branch": "master", "sha256": "0lan6930g5a9z4ack9jj0zdd0mb2s6q2xzpiwcjdc3pvl9b1nbw4", -- cgit v1.2.3