From c8c6c5c68bfcec67b93ca101acd10287ec9f2b47 Mon Sep 17 00:00:00 2001 From: tegwick Date: Thu, 30 Apr 2026 22:52:10 +0200 Subject: [PATCH] fix(build): 4-way split of Generated.Types to stay under 287 MB .hi limit 2-way split (60 entities per part) still crashes: TypesPart1.hi itself reaches 287 MB due to mandatory type-class instance data per IHP entity. 4-way split (~30 entities each, ~150 MB .hi) stays safely under the limit. Co-Authored-By: Claude Sonnet 4.6 --- flake.nix | 76 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 26 deletions(-) diff --git a/flake.nix b/flake.nix index 308e5b6..de34d11 100644 --- a/flake.nix +++ b/flake.nix @@ -99,9 +99,10 @@ # pkgs.ghc = haskellPackages.override { overrides = ihpOverrides }. # We extend pkgs.ghc with a mkDerivation override (lib.mkAfter ensures # we run after IHP's overlay, so prev.ghc is already IHP's package set). + # 2-way split (60 entities) still crashes — TypesPart1.hi itself hits 287 MB. + # 4-way split (~30 entities, ~150 MB .hi each) stays safely under the limit. # When pname == "inter-hub-models", postUnpack replaces the monolithic - # Types.hs with a thin re-export wrapper and ensures TypesPart1/TypesPart2 - # are present and listed in the cabal. + # Types.hs with a thin re-export wrapper and adds TypesPart1-4 to cabal. overlays = lib.mkAfter [ (final: prev: { ghc = prev.ghc.extend (hfinal: hprev: { @@ -109,56 +110,77 @@ let drv = hprev.mkDerivation args; in if (args.pname or "") == "inter-hub-models" then drv.overrideAttrs (old: { - # Splits the monolithic Generated.Types (119 imports, ~287 MB - # interface-file overflow in GHC 9.10.3) into two halves. - # All awk/printf: no external file deps, no git-tracking required. - # -O0 keeps .hi files small (strips unfoldings/specialisations). + # Splits Generated.Types (119 imports, ~287 MB .hi overflow in GHC 9.10.3) + # into four quarters (~30 entities each, ~150 MB .hi — safely under limit). + # -O0 strips unfoldings/specialisations for additional .hi size reduction. configureFlags = (old.configureFlags or []) ++ [ "--ghc-option=-O0" ]; postUnpack = (old.postUnpack or "") + '' _types="$sourceRoot/build/Generated/Types.hs" - # TypesPart1: first half of modules + # TypesPart1: first quarter (~30 entities) awk 'BEGIN{n=0} /^import Generated\./{n++; mods[n]=$2} END{ - half=int(n/2)+1 - print "-- Split: GHC 9.10.3 interface-file overflow workaround." + q=int(n/4)+1 print "module Generated.TypesPart1 (" - for(i=1;i<=half;i++) { - if(i "$sourceRoot/build/Generated/TypesPart1.hs" - # TypesPart2: second half of modules + # TypesPart2: second quarter awk 'BEGIN{n=0} /^import Generated\./{n++; mods[n]=$2} END{ - half=int(n/2)+1 - print "-- Split: GHC 9.10.3 interface-file overflow workaround." + q1=int(n/4)+1; q2=int(n/2)+1 print "module Generated.TypesPart2 (" - for(i=half+1;i<=n;i++) { + for(i=q1+1;i<=q2;i++) { + if(i "$sourceRoot/build/Generated/TypesPart2.hs" + + # TypesPart3: third quarter + awk 'BEGIN{n=0} /^import Generated\./{n++; mods[n]=$2} END{ + q2=int(n/2)+1; q3=int(3*n/4)+1 + print "module Generated.TypesPart3 (" + for(i=q2+1;i<=q3;i++) { + if(i "$sourceRoot/build/Generated/TypesPart3.hs" + + # TypesPart4: fourth quarter + awk 'BEGIN{n=0} /^import Generated\./{n++; mods[n]=$2} END{ + q3=int(3*n/4)+1 + print "module Generated.TypesPart4 (" + for(i=q3+1;i<=n;i++) { if(i "$sourceRoot/build/Generated/TypesPart2.hs" + for(i=q3+1;i<=n;i++) print "import " mods[i] + }' "$_types" > "$sourceRoot/build/Generated/TypesPart4.hs" # Thin wrapper replaces the monolithic Types.hs printf '%s\n' \ - '-- Split wrapper: GHC 9.10.3 interface-file overflow workaround.' \ 'module Generated.Types (' \ ' module Generated.TypesPart1,' \ - ' module Generated.TypesPart2' \ + ' module Generated.TypesPart2,' \ + ' module Generated.TypesPart3,' \ + ' module Generated.TypesPart4' \ ' ) where' \ 'import Generated.TypesPart1' \ 'import Generated.TypesPart2' \ + 'import Generated.TypesPart3' \ + 'import Generated.TypesPart4' \ > "$_types" - # Add TypesPart1/TypesPart2 to cabal exposed-modules and add - # ghc-options: -O0 to keep interface file sizes below the - # GHC 9.10.3 binary-deserialisation limit (~287 MB). - # -O0 strips unfoldings/specialisations from .hi files, - # reducing them by ~10x so TypesPart*.hi stays well under the limit. + # Add TypesPart1-4 to cabal exposed-modules. + # 8-space indent: 4-space would be parsed as a new stanza field. _cabal=$(ls "$sourceRoot"/*.cabal | head -1) if ! grep -q 'Generated\.TypesPart1' "$_cabal"; then awk '/^ exposed-modules:/{ @@ -169,6 +191,8 @@ print print " Generated.TypesPart1" print " Generated.TypesPart2" + print " Generated.TypesPart3" + print " Generated.TypesPart4" next }{print}' "$_cabal" > "$_cabal.new" mv "$_cabal.new" "$_cabal"