From 5378eb881e2e70faf21bfe2e6e7f0a293f896eca Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 1 May 2026 21:51:47 +0200 Subject: [PATCH] fix(nix): split inter-hub-models into two Cabal library components GHC 9.10.3 crashes with Data.Binary.Get.runGet at position 287686318 invariantly when compiling all 476 inter-hub-models modules in a single --make invocation. Split into two library components to force two separate GHC compilations: models-inner (~63 modules): Generated.ActualTypes.* + Generated.Enums Pure type definitions; zero inter-hub-models dependencies. main library (~413 modules): entity ops + Include instances Depends on models-inner. Longer-term this is the right architecture: explicit boundaries reduce build cost, isolate changes, and make diagnostics cheaper. Co-Authored-By: Claude Sonnet 4.6 --- flake.nix | 174 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 144 insertions(+), 30 deletions(-) diff --git a/flake.nix b/flake.nix index b6cc99f..cbc93fd 100644 --- a/flake.nix +++ b/flake.nix @@ -110,42 +110,156 @@ let drv = hprev.mkDerivation args; in if (args.pname or "") == "inter-hub-models" then drv.overrideAttrs (old: { - # Root cause: "module M" re-export syntax in IHP-generated hubs - # causes GHC to embed the FULL interface of M into the hub's .hi. - # Generated.ActualTypes uses "module M" for 61 sub-modules; the - # resulting ActualTypes.hi hits the GHC 9.10.3 Data.Binary.Get - # 274 MB limit (crashes at position 287686318: not enough bytes) - # when WidgetVersionInclude reads it back. Same pattern applied to - # Generated.Types (119 entity re-exports) — made into empty stub - # with direct imports in inter-hub-lib instead. - # Fix for ActualTypes: remove the explicit (module M, ...) export - # list so GHC stores compact name-reference entries in the .hi. + # GHC 9.10.3 crash: Data.Binary.Get.runGet at position 287686318. + # Invariant regardless of flags. Workaround: split the 476-module + # inter-hub-models into two Cabal library components so GHC runs + # two separate --make invocations instead of one giant one. + # + # models-inner (~63 modules): Generated.ActualTypes.* + Enums. + # Pure type definitions; no inter-hub-models deps. + # main library (~413 modules): entity ops + Include instances. + # Depends on models-inner. + # + # Long-term intent: explicit module boundaries reduce build cost, + # isolate changes, and make diagnostics cheaper across the board. configureFlags = (old.configureFlags or []) ++ [ "--ghc-option=-O0" "--ghc-option=-fomit-interface-pragmas" ]; postUnpack = (old.postUnpack or "") + '' - # Strip "module M" re-export syntax from Generated.ActualTypes. - # With 61 "module M" entries GHC embeds every sub-interface - # into ActualTypes.hi, producing a file that hits the GHC 9.10.3 - # Data.Binary.Get 274 MB limit (crashes at position 287686318) - # when WidgetVersionInclude reads it. Removing the explicit - # export list keeps the module semantically identical (all - # imports are still re-exported) but forces GHC to use - # compact name-reference entries instead of embedded copies. - _actual="$sourceRoot/build/Generated/ActualTypes.hs" - awk '/^module Generated\.ActualTypes /{print "module Generated.ActualTypes where"; next} {print}' \ - "$_actual" > "$_actual.new" && mv "$_actual.new" "$_actual" - - _types="$sourceRoot/build/Generated/Types.hs" - printf '%s\n' 'module Generated.Types () where' > "$_types" - - # Remove Generated.Types from exposed-modules: - # reduces HPT entry count by 1 (476 instead of 477). _cabal=$(ls "$sourceRoot"/*.cabal | head -1) - awk '/Generated\.Types$/ && !/TypesPart/{next} {print}' \ - "$_cabal" > "$_cabal.new" - mv "$_cabal.new" "$_cabal" + _pname=$(grep '^name:' "$_cabal" | awk '{print $2}') + + # Classify exposed-modules into inner vs outer. + # Inner: Generated.ActualTypes.X (capital X) and Generated.Enums + # — these have zero inter-hub-models dependencies. + # Outer: everything else except Generated.Types (empty stub). + _inner=$(awk ' + /^ exposed-modules:/{e=1;next} + e && /^ /{m=$1; + if (m~/^Generated\.ActualTypes\.[A-Z]/||m=="Generated.Enums") + print m; + next} + e{e=0} + ' "$_cabal") + _outer=$(awk ' + /^ exposed-modules:/{e=1;next} + e && /^ /{m=$1; + if (!(m~/^Generated\.ActualTypes\.[A-Z]/) && + m!="Generated.Enums" && m!="Generated.Types") + print m; + next} + e{e=0} + ' "$_cabal") + + # Rewrite the cabal file with two library stanzas. + # Hard-coded deps/extensions match IHP default.nix template + # (pinned flake — these won't drift without a flake update). + cat > "$_cabal" < "$sourceRoot/build/Generated/Types.hs" ''; }) else if (args.pname or "") == "inter-hub-lib"