Skip to content

Conversation

@mpscholten
Copy link
Member

Summary

  • Introduces an intermediate appLibPackage that compiles all app modules once as a Cabal library, so entry points (RunProdServer, RunJobs, scripts) only compile 1-2 wrapper modules instead of the entire app
  • For textcontent (157 modules, 2 scripts): 165 vs 628 module compilations, ~74% reduction
  • Dev shell and ghci workflow unchanged

How it works

Before: Each of RunProdServer, RunJobs, and every script independently compiled all ~157 app modules via ghc --make:

modelsPackage -> runServer   (157 modules)
              -> runJobs     (158 modules)  
              -> script1     (157 modules)
              -> script2     (157 modules)

After: App modules are compiled once into a library. Entry points link against it:

modelsPackage -> appLibPackage (162 modules, once)
                    -> binaries: Main.hs (1 module) -> RunProdServer
                                 RunJobs.hs (1 module) -> RunJobs
                                 script wrappers (1 module each)

Key implementation details:

  • appLibSrc generates a .cabal file at build time, discovering modules via find and populating build-depends from ghc-pkg list (filtering out private z-* internal sub-libraries)
  • Config/ modules handled via hs-source-dirs: . Config (matching existing -i. -iConfig from Makefile)
  • Test/ directory and Main.hs excluded from the library
  • Entry point build deletes all .hs except Main.hs so GHC resolves imports from the library package DB instead of recompiling from source
  • All entry points share -odir/-hidir for object file reuse (e.g. RunJobs reuses Main.o)

Test plan

  • nix build produces RunProdServer, RunJobs, and script binaries
  • Library build log shows all ~162 modules compiled
  • Entry point build log shows only 1-2 modules compiled per binary
  • Test on a second IHP project to verify generality

🤖 Generated with Claude Code

Previously, each entry point (RunProdServer, RunJobs, scripts) compiled
the entire app from scratch (~157 modules each). For a project with 2
scripts, that's 157×4 = 628 module compilations.

This introduces an intermediate `appLibPackage` that compiles all app
modules once as a Cabal library. Entry points then compile only their
1-2 wrapper modules and link against the pre-compiled library.

Build graph:
  modelsPackage -> appLibPackage -> binaries (Main.hs + wrappers)

Key implementation details:
- appLibSrc generates a .cabal file at build time, discovering modules
  via find and populating build-depends from ghc-pkg list (filtering
  out private z-* internal sub-libraries)
- Config/ modules handled via hs-source-dirs: . Config (matching the
  existing -i. -iConfig from Makefile)
- Test/ and Main.hs excluded from the library
- Entry point build deletes all .hs except Main.hs so GHC uses the
  library package DB instead of recompiling from source
- Dev shell (allHaskellPackages) unchanged

For textcontent (157 modules, 2 scripts): 165 vs 628 module
compilations, ~74% reduction in compile work.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant