Visitar URL original
Directly call in-library functions to build packages by sheaf · Pull Request #11703 · haskell/cabal · GitHub
Skip to content

Directly call in-library functions to build packages - #11703

Merged
mergify[bot] merged 4 commits into
haskell:masterfrom
sheaf:wip/cabal-install-hooks
Apr 26, 2026
Merged

mergify[bot] merged 4 commits into
haskell:masterfrom
sheaf:wip/cabal-install-hooks

Conversation

@sheaf

@sheaf sheaf commented Apr 3, 2026 •

Copy link
Copy Markdown
Collaborator

This is a rebase of #9871. I had to create a new PR as Matthew is no longer active (and that PR was made from his personal fork).


Template B: This PR does not modify behaviour or interface.

This is a major refactoring in how cabal-install works, with a few knock-on internal changes to some Cabal library functionality.
It should not cause any breakage for users (I tested clc-stackage and it all builds unchanged with this branch).


This PR modifies cabal-install to directly go through the Cabal library when building packages, instead of the Setup interface (except of course for build-type: Custom packages).

In particular, to build a package with build-type: Hooks, cabal-install will compile the package's SetupHooks file into an external hooks executable using the new hooks-exe package. All hooked operations are then performed by communicating with this external executable, instead of going through an opaque Setup executable. The hooks-exe package provides both functionality for compiling an external hooks executable and for interfacing with it. This makes use of the CommunicationHandle API that I added to process.

The main change is to SetupWrapper: the old InternalMethod becomes LibraryMethod, which builds the package by calling Cabal library functions. This is used to build all packages unless build-type: Custom or there is a Cabal library version mismatch which requires us to fall back to compiling Setup.hs and running that. The SelfExec method as well as forceExternalSetupMethod are removed: they no longer have any purpose, as builds can now be carried out concurrently thanks to 7b90583 and edb808a.

The new Distribution.Client.InLibrary module contains all the necessary framework for taking information that cabal-install has and invoking Cabal library functions to perform the appropriate action (configure, build, test, bench, ...). Most of these are pretty simple; the main difficulty is with configure as we need to jump to the part of the Cabal configure code that continues after figuring out information about the system (e.g. compiler information), to avoid wasting work with Cabal rediscovering things that cabal-install already knows. This required a bit of refactoring in Distribution.Simple.Configure.

TODO:

@sheaf
sheaf force-pushed the wip/cabal-install-hooks branch 4 times, most recently from cff8372 to 0a2b46e Compare April 3, 2026 19:08
@sheaf
sheaf force-pushed the wip/cabal-install-hooks branch 6 times, most recently from d100f9c to 72c1495 Compare April 17, 2026 11:04
@sheaf
sheaf marked this pull request as ready for review April 17, 2026 12:23
@sheaf

sheaf commented Apr 17, 2026 •

Copy link
Copy Markdown
Collaborator Author

I now consider this patch ready for review. Significant changes since #9871:

  1. I further refactored the Cabal library configure function so that all the logic that pertains to adjusting BuildOptions based on compiler and toolchain capabilities (e.g. support for bytecode libraries, profiling dynamic, etc) can be shared. This avoids duplicating logic inside cabal-install and prevents the codepaths from going out of sync.
  2. I continued refactoring SetupWrapper, taking into account suggestions by @andreabedini on the original PR (e.g. moving more functions to the top level, see e.g. cabalLibFromOptions and cabalLibVersionToUse). I also took the opportunity to more clearly demarcate v1-only logic. On top of that, I was able to get rid of the self-exec setup method, now that Cabal library: allow setting the logging handle #11077 has landed. So there is only "external" (via Setup executable) and "in library" now.
  3. The test I recently added for custom-setup builds fail when constraining dependencies in the Cabal-hooks package stack #11331 revealed that the way I had set up the hooks-exe implicit dependency in the previous iteration locked out users from using a different Cabal version in setup-depends entirely, as opposed to falling back to the external setup method. So I moved the necessary logic for compiling an external hooks executable into the Cabal library, which I now realise is the only place where it makes sense for it to live. The logic for invoking the external hooks executable remains separate.

I still need to check stackage compilation. With the changes in (1) implemented, I am now significantly more confident that we are not changing behaviour.

@geekosaur geekosaur left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that, despite not intending to change behavior, this probably warrants a changelog entry marked significant.

@sheaf
sheaf force-pushed the wip/cabal-install-hooks branch from 72c1495 to 43fd73b Compare April 20, 2026 09:49
@sheaf

sheaf commented Apr 20, 2026

Copy link
Copy Markdown
Collaborator Author

I think that, despite not intending to change behavior, this probably warrants a changelog entry marked significant.

I've written a changelog entry now, please let me know what you think!

@sheaf sheaf mentioned this pull request Apr 20, 2026
3 tasks done
@sheaf
sheaf force-pushed the wip/cabal-install-hooks branch 2 times, most recently from afeb1b3 to 4cc4945 Compare April 20, 2026 11:00
@sheaf

sheaf commented Apr 20, 2026

Copy link
Copy Markdown
Collaborator Author

I have successfully built all of clc-stackage with this patch (with the exception of the minisat-solver package which fails with a C compiler error that I'm almost certain is unrelated).

I will instrument this version of cabal-install to compare LocalBuildInfo to further validate these changes.

@sheaf

sheaf commented Apr 22, 2026 •

Copy link
Copy Markdown
Collaborator Author

I have spent this morning diffing the output of cabal-install before and after this patch. I have about 2GB of dumped LocalBuildInfo files, and the differences I am seeing are:

  1. ProgramDb
    a. ghc and ghc-pkg are now FoundOnSystem instead of UserSpecified. I think this is expected because we no longer have to rely on --with-ghc flags to communicate the GHC from cabal-install to the Cabal library.
    b. We consistently add data_dir environment variables to the environment of other programs. This is to ensure that build tools invoked at compile time (e.g. a call to alex in a Template Haskell splice) get the proper data directory.
  2. Cosmetic differences in values for configTests, configBenchmarks, configAllowDependingOnPrivateLibs, configCoverageFor. This is just an artifact about where in the code the default values are applied (e.g. whether we see NoFlag or Flag False); I believe it has no incidence on the actual builds.
  3. configInstallDirs: we now pass correct values for flibdir, includedir and mandir, which was not possible via the CLI interface because there are no ConfigFlags for those fields.

I also found a harmless minor inconsistency in targetBuildDepends where we weren't passing "ThisVersion" constraints corresponding to versions found by the solver. I've rectified that, but I don't expect it to make any difference given that we already pin everything by passing exact unit ids for all dependencies.

I've attached dumps of the LocalBuildInfo of Agda for reference, where the only changes are those described above (and some noise from build directories and ABI hashes changing).

Agda-HEAD.txt
Agda-InLib.txt

@philderbeast

Copy link
Copy Markdown
Collaborator

I also found a harmless minor inconsistency in targetBuildDepends where we weren't passing "ThisVersion" constraints corresponding to versions found by the solver. I've rectified that, but I don't expect it to make any difference given that we already pin everything by passing exact unit ids for all dependencies.

Are these some of those differences?

image

It looks like Agda-InLib.txt accepts any version for these.

@sheaf
sheaf force-pushed the wip/cabal-install-hooks branch from 4cc4945 to 29f2db4 Compare April 22, 2026 13:00
@sheaf

sheaf commented Apr 22, 2026

Copy link
Copy Markdown
Collaborator Author

I also found a harmless minor inconsistency in targetBuildDepends where we weren't passing "ThisVersion" constraints corresponding to versions found by the solver. I've rectified that, but I don't expect it to make any difference given that we already pin everything by passing exact unit ids for all dependencies.

Are these some of those differences?
image

It looks like Agda-InLib.txt accepts any version for these.

Yes, that's what I was talking about. I don't think it makes a difference because we pass exact unit IDs for all dependencies (those determined by the solver). I updated this PR to includes the "ThisVersion" constraints determined by the solver just for the sake of consistency.

@sheaf
sheaf force-pushed the wip/cabal-install-hooks branch 2 times, most recently from bb4ba48 to 7a05966 Compare April 23, 2026 11:09
@sheaf

sheaf commented Apr 23, 2026

Copy link
Copy Markdown
Collaborator Author

I believe I've addressed everything. I would appreciate a review on #11731 so that I can land that first; that's a self-contained change to recompilation checking for SetupHooks pre-build rules.

@mergify mergify Bot added ready and waiting Mergify is waiting out the cooldown period merge delay passed Applied (usually by Mergify) when PR approved and received no updates for 2 days queued labels Apr 24, 2026
@mergify

mergify Bot commented Apr 26, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

This pull request spent 1 hour 53 minutes 43 seconds in the queue, including 1 hour 49 minutes 6 seconds running CI.

Required conditions to merge
  • #approved-reviews-by >= 2 [🛡 GitHub branch protection]
  • #changes-requested-reviews-by = 0 [🛡 GitHub branch protection]
  • #review-threads-unresolved = 0 [🛡 GitHub branch protection]
  • any of [🛡 GitHub branch protection]:
    • check-success = Doctest Cabal
    • check-neutral = Doctest Cabal
    • check-skipped = Doctest Cabal
  • any of [🛡 GitHub branch protection]:
    • check-success = Meta checks
    • check-neutral = Meta checks
    • check-skipped = Meta checks
  • any of [🛡 GitHub branch protection]:
    • check-success = docs/readthedocs.org:cabal
    • check-neutral = docs/readthedocs.org:cabal
    • check-skipped = docs/readthedocs.org:cabal
  • any of [🛡 GitHub branch protection]:
    • check-success = Validate post job
    • check-neutral = Validate post job
    • check-skipped = Validate post job
  • any of [🛡 GitHub branch protection]:
    • check-success = fourmolu
    • check-neutral = fourmolu
    • check-skipped = fourmolu
  • any of [🛡 GitHub branch protection]:
    • check-success = hlint
    • check-neutral = hlint
    • check-skipped = hlint
  • any of [🛡 GitHub branch protection]:
    • check-success = Bootstrap post job
    • check-neutral = Bootstrap post job
    • check-skipped = Bootstrap post job
  • any of [🛡 GitHub branch protection]:
    • check-success = whitespace
    • check-neutral = whitespace
    • check-skipped = whitespace
  • any of [🛡 GitHub branch protection]:
    • check-success = Check sdist post job
    • check-neutral = Check sdist post job
    • check-skipped = Check sdist post job
  • any of [🛡 GitHub branch protection]:
    • check-success = Changelogs
    • check-neutral = Changelogs
    • check-skipped = Changelogs

mergify Bot added a commit that referenced this pull request Apr 26, 2026
@mergify
mergify Bot merged commit 9c13a3e into haskell:master Apr 26, 2026
213 checks passed
@mergify mergify Bot removed the queued label Apr 26, 2026
@dcoutts

dcoutts commented Apr 30, 2026

Copy link
Copy Markdown
Contributor

🎉

@andreabedini

Copy link
Copy Markdown
Collaborator

Good work @sheaf and @mpickering!

@zlonast zlonast mentioned this pull request May 13, 2026
2 tasks done
ulysses4ever added a commit to ulysses4ever/cabal that referenced this pull request Oct 14, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (haskell#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
ulysses4ever added a commit to ulysses4ever/cabal that referenced this pull request Oct 14, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (haskell#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
@PaleRoses

Copy link
Copy Markdown
Contributor

hey, heads up, this one made our no-op builds noticeably worse.

elabGPkgDescription is the whole package's GenericPackageDescription, and it now sits in every component's ElaboratedConfiguredPackage. the plan cache serializes both the elaborated and the improved plan, so a package with N components writes its full GPD 2N times, on top of the PackageDescription copies that were already there. that's quadratic in components per package, for a field that's "just used to report errors/warnings".

minimal repro: one package, N public sublibraries, one module each, a 16-flag common stanza. cabal build all once, then a no-op cabal build all under +RTS -s. ghc 9.14.1, macOS arm64, sizes are exact, times are single runs so take them loosely:

sublibs improved-plan, 3.16.1.0 improved-plan, 3.18.1.0 no-op, 3.16.1.0 no-op, 3.18.1.0
10 0.39 MB 0.79 MB 1.19 s 1.55 s
40 3.6 MB 7.5 MB 1.46 s 3.58 s
80 13.3 MB 27.3 MB 1.79 s 2.83 s

on our real project (538 components across 44 packages, the biggest has 60) improved-plan is 56 MB on 3.16.1.0 and 127 MB on 3.18.1.0, same sources. a no-op cabal build on 3.18 allocates 10.9 GB, peaks at 720 MB live and spends more than half its time in GC, about 3 s per invocation, just to tell me there's nothing to build. a bigger -A makes it worse. one of our common warning flags is in the 3.16 cache 21,777 times and in the 3.18 cache 43,137 times.

separately, and i'm not sure it's from this PR: the per-unit configured programs seem to carry more copies of PATH now. the ghcup bin dir shows up 5,149 times in the 3.16 cache and 16,732 times in 3.18's.

we can't just stay on 3.16 either: ghc 9.14.2-rc2 wants jsem protocol v2, and with 3.16's semaphore it says "protocol version mismatch (got v1, supported v2)" and compiles sequentially.

could the GPD be stored once per package, or left out of the cached plan and re-read when a diagnostic actually needs it? happy to test a patch.

ulysses4ever added a commit to ulysses4ever/cabal that referenced this pull request Oct 15, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (haskell#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
ulysses4ever added a commit to ulysses4ever/cabal that referenced this pull request Oct 16, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (haskell#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
@ulysses4ever ulysses4ever mentioned this pull request Oct 16, 2026
6 tasks done
ulysses4ever added a commit that referenced this pull request Oct 16, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
ulysses4ever added a commit that referenced this pull request Oct 16, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
ulysses4ever added a commit that referenced this pull request Oct 17, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. ghc-compat is added at the root of every component, unless the
component depends on it there already, with bounds of its own; a dependency
under a condition, or in another component, is no reason to leave it out.
See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
ulysses4ever added a commit that referenced this pull request Oct 17, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. ghc-compat is added at the root of every component, unless the
component depends on it there already, with bounds of its own; a dependency
under a condition, or in another component, is no reason to leave it out.
A re-export that names the package of a module, array:Data.Array say,
names the fork too, as Cabal resolves it by that name.
See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
ulysses4ever added a commit that referenced this pull request Oct 17, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. ghc-compat is added at the root of every component, unless the
component depends on it there already, with bounds of its own; a dependency
under a condition, or in another component, is no reason to leave it out.
A re-export that names the package of a module, array:Data.Array say,
names the fork too, as Cabal resolves it by that name.
See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
ulysses4ever added a commit that referenced this pull request Oct 18, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. ghc-compat is added at the root of every component, unless the
component depends on it there already, with bounds of its own; a dependency
under a condition, or in another component, is no reason to leave it out.
A re-export that names the package of a module, array:Data.Array say,
names the fork too, as Cabal resolves it by that name.
See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
ulysses4ever added a commit that referenced this pull request Oct 24, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. ghc-compat is added at the root of every component, unless the
component depends on it there already, with bounds of its own; a dependency
under a condition, or in another component, is no reason to leave it out.
A re-export that names the package of a module, array:Data.Array say,
names the fork too, as Cabal resolves it by that name.
See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
ulysses4ever added a commit that referenced this pull request Oct 24, 2026
MicroCabal rewrites the dependencies of every package it builds with mhs:
it adds ghc-compat, which holds modules that are part of base in GHC but
not in mhs, such as GHC.Stack and Language.Haskell.TH.Syntax, and it uses
the forks array-mhs and random-mhs instead of array and random, which mhs
cannot compile. Packages written for GHC rely on both.

Distribution.Simple.MHS.rewritePackageDescription does the same, and
cabal-install applies it to all the packages it gives to the solver
(rewriteSourcePackages, next to the rewriting for allow-newer), so that the
solver chooses ghc-compat and the forks, and cabal-install builds them from
Hackage like any other dependency. Nothing has to be installed for mhs
beforehand, and offline, where they cannot be had, the packages are left as
they are. ghc-compat is added at the root of every component, unless the
component depends on it there already, with bounds of its own; a dependency
under a condition, or in another component, is no reason to leave it out.
A re-export that names the package of a module, array:Data.Array say,
names the fork too, as Cabal resolves it by that name.
See Note [MicroHs rewrites of packages].

Rewriting the packages before solving is enough thanks to a recent change of
cabal-install, which calls Cabal in-library (#11703): it configures a
package with the description of its plan, which is rewritten already,
rather than having a Setup script read the .cabal file of the package
again. Before that change, configure had to rewrite the package once more.
Only a Custom setup script still reads the .cabal file, and mhs cannot
compile one.

As with MicroCabal, the version macros of a fork are also defined under the
name of the package it forks: QuickCheck, which depends on random, tests
MIN_VERSION_random(1,3,0) to define its RandomGen instance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge delay passed Applied (usually by Mergify) when PR approved and received no updates for 2 days merge me Tell Mergify Bot to merge meta: big ready and waiting Mergify is waiting out the cooldown period

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants