Skip to content

Conversation

andreabedini
Copy link
Collaborator

Hello,

this PR adds support for cross-compilation to cabal-install.

In this work cross-compilation is viewed exclusively as a cabal-install feature, and it is implemented without any substantial change to Cabal. I introduced the concept of stages (which I call build and host) with their respective toolchain (compiler and program, package and pkgconfig databases); and re-used the solver concept of scopes to separate executable dependencies from library dependencies. Executable dependencies (like setup scripts and build-tool dependencies) are solved in the build scope, while library dependencies are solved in the host scope. Roughly speaking, the build scope is for binaries you will run on the build machine while the host scope is for binaries you might want to run elsewhere (e.g. on a different platform).

As you can imagine, this involved a fair amount of work and I am well aware that this PR is too big to be considered as a whole. Furthermore I arbitrarily dropped (obscure) feature and (legacy) workarounds to simplify my work. One example being the "independent goals" solver setting: I do not think anybody is using it (or even should use it) and it was interfering with my rework of the solver scopes. That said, I do not think anything prevents bringing these things back, if only lack of time and effort.

While I am proud of how I was able to make everything fit together nicely, the PR is not without issues (Staged is over-engineered, build stage should be entirely optional, IsGraph is just plain awkward, etc). I welcome (and encourage!) a thorough technical review of the implementation.

This work was prompted by IOG's efforts to build GHC with just cabal-install; and for this reason this branch contains a few commits that are unrelated to cross-compilation. I have spent considerable amount of time making sure each commit corresponds to a single small change1; so it should be easy to identify and separate unrelated, unneeded or unwelcome changes.

Also, shame on me, there are no tests; indeed my only testing was building GHC itself. While some tests for the solver should be easy to add, I do not have a clear testing strategy in mind. Changelog and documentation are not done either.

I have no expectations for this to be merged as-in or even with only minor changes. You are welcome to mark this as draft or to consider it a "implementation included" RFC. I will indeed be offline for the next two weeks, so go wild but I will answer any question or comment only later.

Here is an itemised list of changes, produced with the help of Claude.

Core Cross-Compilation Features

Stage System:

  • Add Build and Host stage types to represent compilation targets - 6d741d4
  • Implement Staged wrapper type for stage-aware data structures - 6d741d4
  • Add stage tracking throughout solver and planning systems - c213cea

Toolchain Management:

  • Introduce Toolchain type with separate compiler, program DB, and package databases - dd1bd2a
  • Add configToolchain and configToolchains for multi-stage toolchain setup - dd1bd2a
  • Implement stage-specific program database resolution - ce05b66

Solver Enhancements:

  • Add stage tracking to SolverId and dependency resolution - c213cea
  • Separate executable dependencies (build scope) from library dependencies (host scope) - 97a950d
  • Update constraint system to handle stage-specific constraints - 059f001
  • Modify package path resolution for cross-compilation scenarios - 97a950d

Major Refactoring

Project Planning (ProjectPlanning.hs):

  • Complete rewrite of elaboration process for stage-aware planning - 4b55c76
  • Update instantiateInstallPlan to handle cross-compilation - 69b38ad
  • Refactor dependency resolution to respect stage boundaries - e80cdcb
  • Add stage propagation throughout planning pipeline - e80cdcb

Install Plan:

  • Enhance InstallPlan to track stage information - f8cec88
  • Update dependency consistency checking for cross-compilation - 4015ef0
  • Modify plan execution to use appropriate toolchain per stage - 4b55c76

Build System Integration:

  • Update all build commands (CmdBuild, CmdRepl, CmdExec, etc.) for stage awareness - 4b55c76
  • Modify executable copying and directory management - 95945f7
  • Add stage-specific build directory handling - 19bebc9

Removed/Simplified Features

Legacy Removals:

  • Remove independent goals solver setting - 5ec63e5
  • Drop base-on-base compilation trick - 929676c
  • Remove obsolete build tool workarounds - 086ccc2, db76bd1
  • Eliminate storePackageDBStack mechanism - 929676c
  • Remove in-place building logic - 19bebc9

Simplified Logic:

  • Streamline shared/profiling library selection - f47840d
  • Consolidate package database resolution during planning - ce05b66
  • Remove outdated solver workarounds - db76bd1

Infrastructure Improvements

Error Handling & Logging:

  • Add HasCallStack constraints throughout codebase - aff15c3, 9b7301c
  • Enhance logging in build and setup processes - fdf8b59, e1f578c
  • Improve error messages in solver and validation - a70bb7d
  • Add progress tracking with LogProgress - 94e60c4

Code Quality:

  • Add Pretty instances for new types - f5a1571
  • Improve readability and documentation - 08f604f, 4bc268e
  • Add type safety improvements - c303cc0
  • Enhance testing infrastructure - d975d43

New Configuration Options

Build Options:

  • Add --build-compiler option for specifying build-stage compiler - 085bbd3
  • Extend constraint syntax to support stage qualifiers - 059f001
  • Add stage-aware target selection - a8050fc, 95945f7

Project Configuration:

  • Introduce ProjectConfigToolchain for toolchain settings - dd1bd2a
  • Add stage-specific package database configuration - ce05b66
  • Update configuration parsing for cross-compilation options - 97a950d

File Structure Changes

New Modules:

  • Distribution.Solver.Types.Stage - Core stage definitions - 6d741d4
  • Distribution.Solver.Types.Toolchain - Toolchain types - dd1bd2a
  • Distribution.Client.Toolchain - Toolchain management - dd1bd2a
  • Distribution.Client.ProjectPlanning.Stage - Stage wrapper types - 4b55c76
  • Distribution.Client.Types.GenericReadyPackage - Generalized ready packages - e2b7b85

Major Updates:

  • 27 files in cabal-install-solver for stage-aware solving - 97a950d
  • 40+ files in cabal-install for cross-compilation support - 4b55c76
  • Multiple test files updated for new functionality - d975d43
  • CI/workflow updates for cross-compilation testing - Various commits

Cabal Library Enhancements

Feature Additions:

  • Support for per-file options in extra source files - 20489a7
  • Add generated CMM source support - dfcc65f
  • Improve response file handling - ab0a509
  • Enhanced program signature tracking - 1023f44

Build Improvements:

  • Better GHC integration for cross-compilation - 4f0b69a
  • Improved setup script handling - 2439bf7
  • Enhanced package description processing - a6cab47
  • Updated build target management - Various commits in the series

Dependency & Build Logic:

  • Fix setup dependency resolution for correct stage - 332b6ec
  • Improve package self-dependency detection - 86116e1
  • Better executable dependency handling - 4517220
  • Enhanced build tool dependency management - 65a4fb1

Check list:

Footnotes

  1. and sometimes failed, see the three "all of it" commits.

- Remove QualifiyOptions

Remove QualifyOptions by setting qoSetupIndependent to be always true (the
current default) and qoBaseShim false (this must have been just a hack of some
sort).
principalPP and setupPP seem to have gone unused since
8194fab
…ry, pkgsUseProfilingLibrary, pkgsUseProfilingLibraryShared

We do not want to check the compiler.
…antiatedWith from ElaboratedConfiguredPackage to ElaboratedComponent

Instantiation only makes sense for components.
…g jsem

This is a user problem. User should not enable jsem on a compiler that does not support it.

This change also avoid us to pass the compiler all the way down.

A better approach to restore this functionality would be to defer the application of the parallel strategy.
… validateSolverResult

- add step to Progress
- add MonadFail Progress instance
- refactor validateSolverResult
Merge fromSolverInstallPlan and fromSolverInstallPlanWithProgress.
andreabedini and others added 28 commits August 21, 2025 13:51
Isolate the common logic between building and only downloading.

_Push the ifs up and the loops down_
Determine packages to build in-place by their solver id, not their package id.
Cabal uses a peculiar c program to check if LD supports and should
use -x. To do this, it shells out to GHC to compiler the C file.
This however requires that GHC will not bail out, yet cabal does
not pass --package-db flags to this GHC invocation, and as such we
can run into situations where GHC bails out, especially during GHC
bootstrap phases where not all boot packages are available.

We however do not need GHC to compiler a c program, and can rely
on the C compiler.

Fundamentally cabal does not allow modelling program dependencies
in the program db, as such we must configure gcc first before using
it.

We make a small change to lib:Cabal (specifically the GHC module,
and it's Internal companion) to allow it to configure gcc first,
before trying to configure ld, and thus having gcc in scope while
configuring ld. This removes the need for the awkward ghc
invocation to compiler the test program.
Split the function into multiple ones.
If a node has dangling edges, then the list of missing neighbours cannot be empty.
Exceptions are not nice but this is an obvious invariant. Graph should provide a better API to make this unnecessary.
Not really a fix. I do not know why this happens.
It is duplicate information since we write the program invocation right after.
@mpickering
Copy link
Collaborator

Looks like very impressive and important work @andreabedini, it's great that you and your team have taken on this work. I'm looking forward to digging into these changes together.

Would it be possible to write a cabal proposal to specify the user-facing changes to this patch? It would be good to get some feedback from other user's interested in cross-compilation and to write down the specification which this patch implements. Once a specification is established it will be easier to assess if the patch implements the agreed specification and there are suitable tests in place.

From your description, it seems that someone could test this patch for themselves by using a wasm or javascript compiler and using `cabal-install" "normally"?

Finally, hope you have a great holiday :)

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.

3 participants