tricu

An interpreted language for exploring Tree Calculus
Log | Files | Refs | README | LICENSE

flake.nix (26479B)


      1 {
      2   description = "tricu";
      3 
      4   inputs = {
      5     nixpkgs.url = "github:NixOS/nixpkgs";
      6     browser-wasi = { url = "github:haskell-wasm/browser_wasi_shim/2f86b49dce50916e2984029c535321e34b234229"; flake = false; };
      7     ghc-wasm.url = "github:haskell-wasm/ghc-wasm-meta/8079eb96bbc63823d99ad8acaff0765532625474";
      8     flake-utils.url = "github:numtide/flake-utils";
      9   };
     10 
     11   outputs = { self, nixpkgs, flake-utils, ghc-wasm, browser-wasi }:
     12     flake-utils.lib.eachDefaultSystem (system:
     13       let
     14         pkgs = nixpkgs.legacyPackages.${system};
     15         siteRelease = import ./ext/demo-site/release.nix { inherit pkgs; zigRuntime = self.packages.${system}.tricu-wasm; haskellRuntime = self.packages.${system}.haskellWasmBrowser; };
     16         packageName = "tricu";
     17         containerPackageName = "${packageName}-container";
     18 
     19         # Archived experiments must not enter active build/test source hashes.
     20         tricuSource = pkgs.lib.cleanSourceWith {
     21           name = "tricu-source";
     22           src = self;
     23           filter = path: type:
     24             let
     25               relative = pkgs.lib.removePrefix "${toString self}/" path;
     26               top = builtins.head (pkgs.lib.splitString "/" relative);
     27             in pkgs.lib.cleanSourceFilter path type && builtins.elem top [
     28               "src" "lib" "test" "bench" "bootstrap" "demos" "ext"
     29               "tricu.cabal" "LICENSE" "README.md"
     30             ];
     31         };
     32 
     33         # All Haskell components now share the source-free runtime. The boundary
     34         # check below rejects reintroducing retired frontend files into inputs.
     35         runtimeSource = tricuSource;
     36 
     37         haskellPackages = pkgs.haskellPackages;
     38         hsLib = pkgs.haskell.lib;
     39 
     40         staticPkgs = pkgs.pkgsStatic;
     41         staticHaskellPackages = staticPkgs.haskellPackages;
     42         staticHsLib = staticPkgs.haskell.lib;
     43 
     44         tricuMuslStatic =
     45           staticHsLib.justStaticExecutables (
     46             staticHsLib.dontCheck (
     47               staticHaskellPackages.callCabal2nix packageName runtimeSource {}
     48             )
     49           );
     50 
     51         tricuStatic = pkgs.runCommand "${packageName}-static-upx" {
     52           nativeBuildInputs = [ pkgs.upx ];
     53         } ''
     54             mkdir -p $out/bin
     55             cp ${tricuMuslStatic}/bin/tricu $out/bin/tricu
     56             chmod +w $out/bin/tricu
     57 
     58             # Good compression, slower build.
     59             upx --best --lzma $out/bin/tricu
     60 
     61             chmod 755 $out/bin/tricu
     62           '';
     63 
     64         tricuPackageTests =
     65           haskellPackages.callCabal2nix packageName tricuSource {};
     66 
     67         tricuPackage =
     68           hsLib.dontCheck (
     69             haskellPackages.callCabal2nix packageName runtimeSource {}
     70           );
     71 
     72         # Compile once with the packaged stable compiler. Runtime needs neither
     73         # source lookup nor a writable compiler/application cache.
     74         arboricxServerBundle = pkgs.runCommand "arboricx-server-bundle" {
     75           nativeBuildInputs = [ tricuPackage ];
     76         } ''
     77           mkdir -p $out
     78           tricu arboricx compile --file ${./.}/tricu-apps/arboricxServer.tri --store build-store \
     79             --output $out/server.arboricx --compile-timeout 900 \
     80             +RTS -N1 -M8192m -RTS
     81           tricu arboricx import --file $out/server.arboricx --store $out/runtime \
     82             > import.log
     83           sed -n 's/^Created module alias .* -> //p' import.log > $out/manifest
     84           test "$(wc -l < $out/manifest)" -eq 1
     85           grep -Eq '^[0-9a-f]{64}$' $out/manifest
     86         '';
     87 
     88         arboricxServerRunner = pkgs.writeShellScriptBin "arboricx-server" ''
     89           exec ${tricuPackage}/bin/tricu arboricx run \
     90             --module "$(${pkgs.coreutils}/bin/cat ${arboricxServerBundle}/manifest)" --name main \
     91             --store ${arboricxServerBundle}/runtime \
     92             --io --no-result --allow-read ./store --allow-write ./store \
     93             +RTS -N1 -M2048m -RTS
     94         '';
     95 
     96         tricuBench =
     97           (hsLib.overrideCabal
     98             (hsLib.doBenchmark (hsLib.dontCheck (
     99               haskellPackages.callCabal2nix packageName runtimeSource {}
    100             )))
    101             (oldAttrs: {
    102               postInstall = (oldAttrs.postInstall or "") + ''
    103                 mkdir -p $out/bin
    104                 cp dist/build/tricu-bench/tricu-bench $out/bin/
    105               '';
    106             })).overrideAttrs (_: {
    107               # No implicit timing run without a caller-selected worker bundle.
    108               benchmarkPhase = "true";
    109             });
    110 
    111         # Opt-in application build, never part of the fast gate. Retain the exact
    112         # sources, compiler metadata and receipts beside the worker bundle.
    113         benchmarkWorkers = pkgs.runCommand "tricu-benchmark-workers" {
    114           nativeBuildInputs = [ tricuPackage ];
    115         } ''
    116           mkdir -p $out/workspace
    117           ${pkgs.lib.concatMapStringsSep "\n" (name:
    118             "cp ${./lib}/${name}.tri $out/workspace/${name}.tri") [
    119               "base" "prelude" "bytes" "conversions" "io" "parser" "http" "socket"
    120             ]}
    121           cp ${./lib/math/native.tri} $out/workspace/native_math.tri
    122           cp ${./lib/intensionalContracts.tri} $out/workspace/intensional.tri
    123           cp ${./lib/arboricx/server.tri} $out/workspace/server.tri
    124           cp ${./bench/workers.tri} $out/workspace/workers.tri
    125           cp ${./bootstrap/stable-compiler.json} $out/compiler.json
    126           timeout 930 tricu arboricx compile --file $out/workspace/workers.tri \
    127             --store $out/store --output $out/workers.arboricx --compile-timeout 900 \
    128             +RTS -N1 -M8192m -RTS
    129           sha256sum $out/workers.arboricx | cut -d ' ' -f 1 > $out/sha256
    130         '';
    131 
    132         # ------------------------------------------------------------------
    133         # Zig Arboricx host
    134         # ------------------------------------------------------------------
    135         tricuZig = pkgs.stdenv.mkDerivation {
    136           pname = "tricu-zig";
    137           version = "0.1.0";
    138           src = ./ext/zig;
    139           passthru.optimizationMode = "ReleaseSafe";
    140           nativeBuildInputs = [ pkgs.zig pkgs.pkg-config ];
    141           buildInputs = [ pkgs.libuv ];
    142           buildPhase = ''
    143             export ZIG_GLOBAL_CACHE_DIR=$TMPDIR/zig-cache
    144             zig build -Doptimize=ReleaseSafe
    145             zig build test -Doptimize=ReleaseSafe
    146             zig build test -Doptimize=Debug
    147           '';
    148           installPhase = ''
    149             mkdir -p $out/bin $out/lib $out/include
    150             cp zig-out/bin/* $out/bin/ 2>/dev/null || true
    151             cp zig-out/lib/* $out/lib/ 2>/dev/null || true
    152             cp include/arboricx.h $out/include/
    153           '';
    154         };
    155 
    156         tricuWasm = pkgs.stdenv.mkDerivation {
    157           pname = "tricu-wasm";
    158           version = "0.1.0";
    159           src = ./ext/zig;
    160           nativeBuildInputs = [ pkgs.zig ];
    161           buildPhase = ''
    162             export ZIG_GLOBAL_CACHE_DIR=$TMPDIR/zig-cache
    163             zig build wasm
    164           '';
    165           installPhase = ''
    166             mkdir -p $out/lib
    167             cp zig-out/bin/arboricx.wasm $out/lib/
    168           '';
    169         };
    170 
    171         tricuWasmTests = pkgs.runCommand "tricu-wasm-tests" {
    172           nativeBuildInputs = [ pkgs.nodejs pkgs.typescript ];
    173         } ''
    174           cp -r ${tricuSource} source
    175           chmod -R u+w source
    176           cd source
    177           tsc -p ext/demo-site/tsconfig.json
    178           timeout 120 node ext/wasm/check.mjs ${tricuWasm}/lib/arboricx.wasm
    179           timeout 120 node ext/wasm/backend-check.mjs ${tricuWasm}/lib/arboricx.wasm
    180           mkdir -p $out
    181         '';
    182 
    183         tricuWasmBrowser = pkgs.runCommand "tricu-wasm-browser" {
    184           nativeBuildInputs = [ pkgs.nodejs pkgs.typescript pkgs.chromium ];
    185         } ''
    186           export HOME=$TMPDIR/home
    187           mkdir -p "$HOME" $out
    188           cp -r ${tricuSource} source
    189           chmod -R u+w source
    190           cd source
    191           tsc -p ext/demo-site/tsconfig.json
    192           timeout 240 node ext/wasm/browser.mjs ${tricuWasm}/lib/arboricx.wasm chromium $out/report.json
    193         '';
    194 
    195         tricuWasmComparison = pkgs.runCommand "tricu-wasm-comparison" {
    196           nativeBuildInputs = [ pkgs.nodejs pkgs.typescript ];
    197         } ''
    198           cp -r ${tricuSource} source
    199           chmod -R u+w source
    200           cd source
    201           tsc -p ext/demo-site/tsconfig.json
    202           mkdir -p $out
    203           node ext/wasm/compare.mjs ${tricuWasm}/lib/arboricx.wasm $out/report.json
    204           node ext/wasm/preview-compare.mjs ${tricuWasm}/lib/arboricx.wasm $out/preview-report.json
    205         '';
    206 
    207         # Explicit, Linux-only smoke measurements; not a timing threshold gate.
    208         tricuZigPerformanceSmoke = pkgs.stdenv.mkDerivation {
    209           pname = "tricu-zig-performance-smoke";
    210           version = "0.1.0";
    211           src = ./.;
    212           nativeBuildInputs = [ pkgs.gcc pkgs.python3 ];
    213           buildPhase = ''
    214             gcc -O2 -Wall -Wextra -Werror ext/zig/tests/performance_smoke.c \
    215               -DARB_HOST_MODE='"${tricuZig.optimizationMode}"' \
    216               -I ${tricuZig}/include -L ${tricuZig}/lib -larboricx \
    217               -Wl,-rpath,${tricuZig}/lib -o performance-smoke
    218           '';
    219           installPhase = ''
    220             mkdir -p $out/bin
    221             cp performance-smoke $out/bin/
    222             python3 ext/zig/tests/performance_smoke.py \
    223               $out/bin/performance-smoke test/fixtures $out/report.json
    224           '';
    225         };
    226 
    227         tricuZigTests = pkgs.stdenv.mkDerivation {
    228           pname = "tricu-zig-tests";
    229           version = "0.1.0";
    230           src = ./.;
    231           nativeBuildInputs = [ pkgs.gcc pkgs.python3 tricuZig ];
    232           buildInputs = [ pkgs.libuv ];
    233           buildPhase = "true";
    234           doCheck = true;
    235           checkPhase = ''
    236             export LD_LIBRARY_PATH=${tricuZig}/lib:$LD_LIBRARY_PATH
    237             ulimit -s 32768
    238 
    239             cd ext/zig
    240 
    241             gcc -Wall -Wextra -Werror -o /tmp/handles_test tests/handles_test.c \
    242               -I ${tricuZig}/include -L ${tricuZig}/lib -larboricx \
    243               -Wl,-rpath,${tricuZig}/lib
    244             /tmp/handles_test
    245 
    246             test "$(tricu-zig --type string ../../test/fixtures/id.arboricx hello)" = hello
    247             test "$(tricu-zig --type string --kernel ../../test/fixtures/append.arboricx 'Hello, ' 'world!')" = 'Hello, world!'
    248 
    249             # C ABI smoke test
    250             gcc -o /tmp/c_abi_test tests/c_abi_test.c \
    251               -I ${tricuZig}/include -L ${tricuZig}/lib -larboricx \
    252               -Wl,-rpath,${tricuZig}/lib
    253             /tmp/c_abi_test
    254 
    255             # IO protocol shape test
    256             gcc -o /tmp/io_protocol_test tests/io_protocol_test.c \
    257               -I ${tricuZig}/include -L ${tricuZig}/lib -larboricx \
    258               -Wl,-rpath,${tricuZig}/lib
    259             /tmp/io_protocol_test
    260 
    261             # IO run test (synchronous driver)
    262             gcc -o /tmp/io_run_test tests/io_run_test.c \
    263               -I ${tricuZig}/include -L ${tricuZig}/lib -larboricx \
    264               -Wl,-rpath,${tricuZig}/lib
    265             /tmp/io_run_test
    266 
    267             # Kernel path append test
    268             gcc -o /tmp/c_abi_append_test tests/c_abi_append_test.c \
    269               -I ${tricuZig}/include -L ${tricuZig}/lib -larboricx \
    270               -Wl,-rpath,${tricuZig}/lib
    271             /tmp/c_abi_append_test
    272 
    273             # Native bundle tests
    274             gcc -o /tmp/native_bundle_append_test tests/native_bundle_append_test.c \
    275               -I ${tricuZig}/include -L ${tricuZig}/lib -larboricx \
    276               -Wl,-rpath,${tricuZig}/lib
    277             /tmp/native_bundle_append_test
    278 
    279             gcc -o /tmp/native_bundle_id_test tests/native_bundle_id_test.c \
    280               -I ${tricuZig}/include -L ${tricuZig}/lib -larboricx \
    281               -Wl,-rpath,${tricuZig}/lib
    282             /tmp/native_bundle_id_test
    283 
    284             gcc -o /tmp/native_bundle_bools_test tests/native_bundle_bools_test.c \
    285               -I ${tricuZig}/include -L ${tricuZig}/lib -larboricx \
    286               -Wl,-rpath,${tricuZig}/lib
    287             /tmp/native_bundle_bools_test
    288 
    289             # Python FFI test
    290             ARBORICX_LIB=${tricuZig}/lib/libarboricx.so \
    291               python3 tests/python_ffi_test.py
    292 
    293             mkdir -p $out
    294             echo "All Zig tests passed" > $out/result
    295           '';
    296         };
    297 
    298         # ------------------------------------------------------------------
    299         # PHP FFI host
    300         # ------------------------------------------------------------------
    301         tricuPhp = pkgs.stdenv.mkDerivation {
    302           pname = "tricu-php";
    303           version = "0.1.0";
    304           src = ./ext/php;
    305           nativeBuildInputs = [ pkgs.makeWrapper phpWithFfi tricuZig ];
    306           buildPhase = "true";
    307           installPhase = ''
    308             mkdir -p $out/share/tricu-php $out/lib $out/bin
    309             cp -r src public run.php $out/share/tricu-php/
    310             cp ${tricuZig}/lib/libarboricx.so $out/lib/
    311             cp ${tricuZig}/include/arboricx.h $out/share/tricu-php/
    312 
    313             makeWrapper ${phpWithFfi}/bin/php $out/bin/tricu-php \
    314               --add-flags "$out/share/tricu-php/run.php" \
    315               --set ARBORICX_LIB "$out/lib/libarboricx.so" \
    316               --prefix LD_LIBRARY_PATH : "$out/lib"
    317           '';
    318         };
    319 
    320         # ------------------------------------------------------------------
    321         # JS FFI host
    322         # ------------------------------------------------------------------
    323         tricuJs = pkgs.buildNpmPackage {
    324           pname = "tricu-js";
    325           version = "0.1.0";
    326           src = ./ext/js;
    327           npmDepsHash = "sha256-81C7tsNcbyZVhm3uqiWdDQxp5LAXXO9aueHdMDztCfM=";
    328           nativeBuildInputs = [ pkgs.nodejs tricuZig ];
    329           dontNpmBuild = true;
    330           installPhase = ''
    331             mkdir -p $out/lib/
    332             cp -r . $out/lib/
    333             cp ${tricuZig}/lib/libarboricx.so $out/lib/src
    334           '';
    335         };
    336 
    337         # ------------------------------------------------------------------
    338         # JS FFI host tests (separate target)
    339         # ------------------------------------------------------------------
    340         tricuJsTests = pkgs.stdenv.mkDerivation {
    341           pname = "tricu-js-tests";
    342           version = "0.1.0";
    343           src = ./.;
    344           nativeBuildInputs = [ pkgs.nodejs tricuZig ];
    345           buildPhase = "true";
    346           doCheck = true;
    347           checkPhase = ''
    348             export ARBORICX_LIB=${tricuZig}/lib/libarboricx.so
    349             export LD_LIBRARY_PATH=${tricuZig}/lib:$LD_LIBRARY_PATH
    350             ulimit -s 32768
    351 
    352             cd ext/js
    353             # node_modules are pre-fetched by buildNpmPackage; copy them in
    354             cp -r ${tricuJs}/lib/node_modules .
    355             npm test
    356 
    357             mkdir -p $out
    358             echo "All JS tests passed" > $out/result
    359           '';
    360         };
    361 
    362         # ------------------------------------------------------------------
    363         # PHP FFI tests (separate target)
    364         # ------------------------------------------------------------------
    365         phpWithFfi = pkgs.php.withExtensions (exts: [ pkgs.phpExtensions.ffi ]);
    366 
    367         tricuPhpTests = pkgs.stdenv.mkDerivation {
    368           pname = "tricu-php-tests";
    369           version = "0.1.0";
    370           src = ./.;
    371           nativeBuildInputs = [ phpWithFfi tricuPhp ];
    372           buildPhase = "true";
    373           doCheck = true;
    374           checkPhase = ''
    375             export ARBORICX_LIB=${tricuPhp}/lib/libarboricx.so
    376             export LD_LIBRARY_PATH=${tricuPhp}/lib:$LD_LIBRARY_PATH
    377             ulimit -s 32768
    378 
    379             php ext/php/tests/ownership.php
    380 
    381             # Run PHP host against fixture bundles
    382             php ext/php/run.php run test/fixtures/id.arboricx hello
    383             php ext/php/run.php run test/fixtures/append.arboricx "Hello, " "world!"
    384             php ext/php/run.php run test/fixtures/true.arboricx
    385             php ext/php/run.php run test/fixtures/false.arboricx
    386             php ext/php/run.php run test/fixtures/notQ.arboricx "t t t"
    387 
    388             mkdir -p $out
    389             echo "All PHP tests passed" > $out/result
    390           '';
    391         };
    392       in {
    393         packages.haskellWasmBrowser = import ./ext/haskell-wasm/browser.nix {
    394           inherit pkgs;
    395           probe = self.packages.${system}.haskellWasmProbe;
    396           toolchain = ghc-wasm.packages.${system}.all_9_14;
    397           shimSource = browser-wasi;
    398         };
    399         packages.haskellWasmProbe = import ./ext/haskell-wasm/build.nix {
    400           inherit pkgs;
    401           toolchain = ghc-wasm.packages.${system}.all_9_14;
    402         };
    403         packages.${packageName} = tricuPackage;
    404         packages.default = tricuPackage;
    405         # Independent Haskell oracle for the opt-in published-library consumer.
    406         # Opt-in Linux paired runtime measurements. Build first; timing runs
    407         # explicitly outside the Nix build, never during the fast check gate.
    408         packages.reducerComparison = pkgs.stdenv.mkDerivation {
    409           pname = "tricu-reducer-comparison";
    410           version = "0.1.0";
    411           src = ./.;
    412           nativeBuildInputs = [ pkgs.gcc pkgs.makeWrapper
    413             (haskellPackages.ghcWithPackages (p: [
    414               p.cryptonite p.memory p.base16-bytestring p.vector
    415             ])) ];
    416           buildPhase = ''
    417             ghc -O2 -threaded -rtsopts -XOverloadedStrings -XScopedTypeVariables \
    418               -XMultiWayIf -XLambdaCase -isrc -outputdir objects \
    419               bench/ReducerSmoke.hs -o haskell-reducer-smoke
    420             gcc -O2 -Wall -Wextra -Werror ext/zig/tests/performance_smoke.c \
    421               -DARB_HOST_MODE='"${tricuZig.optimizationMode}"' \
    422               -I ${tricuZig}/include -L ${tricuZig}/lib -larboricx \
    423               -Wl,-rpath,${tricuZig}/lib -o zig-reducer-smoke
    424           '';
    425           doCheck = true;
    426           checkPhase = ''
    427             for kind in id append deep distribution; do
    428               fixture=test/fixtures/id.arboricx
    429               if [ "$kind" = append ]; then fixture=test/fixtures/append.arboricx; fi
    430               ./haskell-reducer-smoke "$kind" 64 2 "$fixture" +RTS -N1 -M512m -K8m -RTS
    431               ./zig-reducer-smoke "$kind" 64 2 "$fixture"
    432             done
    433             # A real but wrong worker must fail complete-result validation.
    434             if ./haskell-reducer-smoke id 256 1 test/fixtures/append.arboricx \
    435                 +RTS -N1 -M512m -K8m -RTS >bad-haskell.log 2>&1; then exit 1; fi
    436             grep -q 'incorrect complete result' bad-haskell.log
    437             if ./zig-reducer-smoke id 256 1 test/fixtures/append.arboricx \
    438                 >bad-zig.log 2>&1; then exit 1; fi
    439             grep -q 'failed:' bad-zig.log
    440           '';
    441           installPhase = ''
    442             mkdir -p $out/bin $out/libexec
    443             cp haskell-reducer-smoke zig-reducer-smoke $out/bin/
    444             cp test/reducer_comparison.py test/process_rss.py $out/libexec/
    445             ghc --version > $out/compiler-version
    446             makeWrapper ${pkgs.python3}/bin/python3 $out/bin/compare-reducers \
    447               --add-flags "$out/libexec/reducer_comparison.py $out/bin/haskell-reducer-smoke $out/bin/zig-reducer-smoke ${./test/fixtures}"
    448           '';
    449           meta.mainProgram = "compare-reducers";
    450         };
    451         packages.inspectorOracle = pkgs.runCommand "arboricx-inspector-oracle" {
    452           nativeBuildInputs = [ (haskellPackages.ghcWithPackages (p: [
    453             p.cryptonite p.memory p.base16-bytestring p.vector
    454           ])) ];
    455         } ''
    456           mkdir -p $out/bin
    457           ghc -O1 -XOverloadedStrings -XScopedTypeVariables -XMultiWayIf -XLambdaCase \
    458             -i${./src} -outputdir "$TMPDIR/objects" ${./test/InspectorOracle.hs} \
    459             -o $out/bin/inspector-oracle
    460         '';
    461         packages.arboricxArtwork = import ./ext/demo-site/artwork.nix { inherit pkgs; };
    462         packages.arboricxWebkitCheck = import ./ext/demo-site/webkit.nix { inherit pkgs; };
    463         packages.arboricxSite = siteRelease.arboricxSite;
    464         packages.arboricxSiteDeps = siteRelease.arboricxSiteDeps;
    465 				packages.tricu-static = tricuMuslStatic;
    466 				packages.tricu-static-upx = tricuStatic;
    467         packages.tricu-bench = tricuBench;
    468         packages.benchmarkWorkers = benchmarkWorkers;
    469         packages.benchmarkSmoke = pkgs.runCommand "tricu-benchmark-smoke" {} ''
    470           ${tricuBench}/bin/tricu-bench ${benchmarkWorkers}/workers.arboricx \
    471             "$(cat ${benchmarkWorkers}/sha256)" --verify-only +RTS -N1 -M8192m -RTS
    472           if ${tricuBench}/bin/tricu-bench ${benchmarkWorkers}/workers.arboricx \
    473               "$(printf '%064d' 0)" --verify-only >bad-hash.log 2>&1; then
    474             echo "benchmark accepted a wrong digest" >&2; exit 1
    475           fi
    476           grep -q 'SHA256 mismatch' bad-hash.log
    477           if ${tricuBench}/bin/tricu-bench ${./test/fixtures/id.arboricx} \
    478               "$(sha256sum ${./test/fixtures/id.arboricx} | cut -d ' ' -f 1)" \
    479               --verify-only >bad-interface.log 2>&1; then
    480             echo "benchmark accepted the wrong interface" >&2; exit 1
    481           fi
    482           grep -q 'exactly the benchmark export interface' bad-interface.log
    483           printf 'not a bundle' > malformed.arboricx
    484           if ${tricuBench}/bin/tricu-bench malformed.arboricx \
    485               "$(sha256sum malformed.arboricx | cut -d ' ' -f 1)" \
    486               --verify-only >malformed.log 2>&1; then
    487             echo "benchmark accepted authenticated malformed bytes" >&2; exit 1
    488           fi
    489           ${tricuBench}/bin/tricu-bench ${benchmarkWorkers}/workers.arboricx \
    490             "$(cat ${benchmarkWorkers}/sha256)" --list >names
    491           test "$(grep -c '^contracts/' names)" -eq 12
    492           test "$(grep -c '^registry-hash/' names)" -eq 15
    493           ${tricuBench}/bin/tricu-bench ${benchmarkWorkers}/workers.arboricx \
    494             "$(cat ${benchmarkWorkers}/sha256)" --iters 1 +RTS -N1 -M8192m -RTS
    495           mkdir -p $out
    496         '';
    497 
    498         packages.tricu-zig = tricuZig;
    499         packages.tricu-wasm = tricuWasm;
    500         packages.tricu-wasm-tests = tricuWasmTests;
    501         packages.tricu-wasm-browser = tricuWasmBrowser;
    502         packages.tricu-wasm-comparison = tricuWasmComparison;
    503         packages.tricu-zig-tests = tricuZigTests;
    504         packages.tricu-zig-performance-smoke = tricuZigPerformanceSmoke;
    505         packages.tricu-php = tricuPhp;
    506         packages.tricu-php-tests = tricuPhpTests;
    507         packages.tricu-js = tricuJs;
    508         packages.tricu-js-tests = tricuJsTests;
    509 
    510         checks.runtimeBoundary = pkgs.runCommand "tricu-runtime-source-boundary" {} ''
    511           test -f ${runtimeSource}/src/Runtime/Tree.hs
    512           for path in Research.hs Eval.hs FileEval.hs Lexer.hs Parser.hs \
    513               Frontend Module/Resolver.hs Module/Workspace.hs; do
    514             test ! -e "${runtimeSource}/src/$path"
    515           done
    516           mkdir -p $out
    517         '';
    518 
    519         checks.${packageName} = tricuPackageTests;
    520         checks.default = tricuPackageTests;
    521         # Fast packaging/filesystem checks only, no compiler or application runs.
    522         checks.arboricxSite = pkgs.runCommand "arboricx-site-release-check" {
    523           nativeBuildInputs = [ pkgs.python3 ];
    524         } ''
    525           python3 ${./ext/demo-site/test/asset-release.py} ${./ext/demo-site/asset-release.py}
    526           python3 ${./ext/demo-site/test/release.py} \
    527             --site ${siteRelease.arboricxSite} --deps ${siteRelease.arboricxSiteDeps} \
    528             --seeder ${./ext/demo-site/seed.py}
    529           mkdir -p $out
    530         '';
    531         checks.bootstrapRetention = pkgs.runCommand "tricu-bootstrap-retention-tests" {
    532           nativeBuildInputs = [ pkgs.python3 ];
    533         } ''
    534           export PYTHONDONTWRITEBYTECODE=1
    535           cd ${tricuSource}
    536           python3 test/bootstrap_retention_test.py
    537           mkdir -p $out
    538         '';
    539         checks.hostPolicy = pkgs.stdenv.mkDerivation {
    540           pname = "tricu-host-policy-tests";
    541           version = "0.1.0";
    542           src = tricuSource;
    543           nativeBuildInputs = [ pkgs.python3 tricuPackage ];
    544           buildPhase = "true";
    545           doCheck = true;
    546           checkPhase = ''
    547             test ! -e archive
    548             export PYTHONDONTWRITEBYTECODE=1
    549             python3 test/unit.py
    550             python3 test/shipped_compiler.py
    551             python3 test/executable_arguments.py ${tricuPackage}/bin/tricu
    552             python3 test/package_registry.py ${tricuPackage}/bin/tricu
    553             python3 test/project_dependencies.py ${tricuPackage}/bin/tricu
    554             python3 test/project_inspection.py ${tricuPackage}/bin/tricu
    555             python3 test/interaction_inspection.py ${tricuPackage}/bin/tricu
    556             python3 test/provenance.py ${tricuPackage}/bin/tricu
    557           '';
    558           installPhase = "mkdir -p $out";
    559         };
    560         # Source compilation and bootstrap generations remain explicit opt-in
    561         # jobs. Haskell package tests above gate the supported host.
    562 
    563         devShells.default = pkgs.mkShell {
    564           buildInputs = with pkgs; [
    565             haskellPackages.cabal-install
    566             haskellPackages.ghc-events
    567             haskellPackages.ghcid
    568             haskellPackages.ghc
    569             upx
    570             gcc
    571             python3
    572           ];
    573 
    574           inputsFrom = [
    575             tricuPackage
    576             tricuZig
    577             tricuPhp
    578           ];
    579         };
    580 
    581         packages.${containerPackageName} = pkgs.dockerTools.buildImage {
    582           name = "tricu";
    583           tag = "latest";
    584 
    585           copyToRoot = pkgs.buildEnv {
    586             name = "image-root";
    587             paths = [ tricuStatic ];
    588             pathsToLink = [ "/bin" ];
    589           };
    590 
    591           config = {
    592             Cmd = [ "/bin/tricu" ];
    593             WorkingDir = "/app";
    594           };
    595         };
    596 
    597         # Opt-in: compiles and exercises the actual deployment entrypoint.
    598         packages.arboricxServerTest = pkgs.runCommand "arboricx-server-test" {
    599           nativeBuildInputs = [ pkgs.python3 ];
    600         } ''
    601           python3 ${./test/arboricx_deployment.py} ${arboricxServerRunner}/bin/arboricx-server
    602           mkdir -p $out
    603         '';
    604         packages.arboricxServerBundle = arboricxServerBundle;
    605         packages.arboricxServerRunner = arboricxServerRunner;
    606         packages.arboricxServer = pkgs.dockerTools.buildImage {
    607           name = "arboricx-server";
    608           tag = "latest";
    609 
    610           copyToRoot = pkgs.runCommand "arboricx-server-root" {} ''
    611             mkdir -p $out/app/store
    612           '';
    613 
    614           config = {
    615             Entrypoint = [ "${arboricxServerRunner}/bin/arboricx-server" ];
    616             WorkingDir = "/app";
    617             ExposedPorts = { "8080/tcp" = {}; };
    618           };
    619         };
    620       });
    621 }