Skip to content

MicroHS support - #11392

Open
ulysses4ever wants to merge 6 commits into
masterfrom
recognize-mhs
Open

ulysses4ever wants to merge 6 commits into
masterfrom
recognize-mhs

Conversation

@ulysses4ever

@ulysses4ever ulysses4ever commented Jan 6, 2026 •

Copy link
Copy Markdown
Collaborator

What's achieved

lib:Cabal and cabal-install can build, run, test and install packages with MicroHs, mhs, the way its own package manager, mcabal, does.

AI disclosure: this was written largely by Claude Code (Fable 5.1 or Opus 5.5 depending on the weather outside). I did review it myself and it's a result of many iterations...

Size

The patch looks big but fear not: most of it is contained in the new MHS.hs module and tests.

Files Lines added Lines removed
Total 73 1362 21
cabal-install 4 47 11
Cabal, except MHS.hs 9 38 2
Distribution.Simple.MHS (MHS.hs) 1 915 0
cabal-testsuite 31 277 0
Docs, with the changelog 28 85 8

(Counted with git diff --numstat against master at aeac769.)

Notes:

  • The most annoying part and the one to focus your review on is cabal-install. In an ideal world, we'd not need to touch it at all but as the Commit history (below) explains, concessions had to be made.

  • Changes to Cabal outside MHS.hs are trivial, which is a strong point.

  • Of the lines added to cabal-testsuite, 57 are in the 4 test scripts and 220 in the packages they build.

  • 22 of the 28 files in docs are the generated help texts of the commands, which gain --mhs and the mhs program.

Commit history

Each commit ends with how many of the 91 packages of MicroHs's own list build, and run a program, with cabal at that commit, replayed for every commit in the same way, with nothing installed for mhs besides its base (see Testing below and all results in a separate repo).

  • 2e55647 Adds the mhs program and the backend Distribution.Simple.MHS, to which configure, build, install and register dispatch: it builds libraries into mhs package files and executables against them, compressed with -z as mcabal does, and installs and registers both, in place and in the store; the packages of mhs itself are described with mhs -L. Every extension is accepted, as mhs ignores the pragmas of those it lacks, and so are reexported-modules, as mhs sees every module of the packages it loads. 3/91 build and run: with base at 0.16.9.0, the solver rejects most packages from Hackage, or falls back to ancient versions of them, until the next commit.
  • 1b449d1 Presents the base of mhs at version 4.19.1.0, and the packages it bundles, such as bytestring and text, as installed packages at the versions MicroCabal uses, except hashable, at 1.4.7.0, the API that mhs implements, rather than MicroCabal's 1.0.0.0. 39/91 build and run.
  • 6528204 Passes the macros of cabal_macros.h, such as MIN_VERSION_base, to the preprocessor of mhs as -D options. 58/91 build and run.
  • b289e8f Rewrites, in cabal-install, the packages that the solver sees, as MicroCabal does: ghc-compat, which has the modules that are part of base with GHC, becomes a dependency of every component, and array and random become their forks array-mhs and random-mhs, so that cabal plans and builds all three from Hackage, and mhs needs nothing installed besides base. Rewriting what the solver sees is enough because cabal-install now builds packages in-library (#11703): it configures each package with the description in its plan, which is rewritten already, rather than running a Setup script that would read the original .cabal file again. 88/91 build and run.
  • cbfde3d Puts the C sources of a library beside its package file, where mhs compiles them when it links a program, and gives mhs the include directories of the dependencies with -I. 91/91 build and run.
  • 5bcc71f cabal install --lib installs libraries and their dependencies with mhs -Q into the directory where mcabal installs packages, which is the package directory of mhs itself after make minstall, or into the one given with --package-env, so that plain mhs finds them, rather than writing a GHC environment file. 91/91 build and run.

Testing: 3 parts

Part 1: cabal-testsuite

cabal-testsuite/PackageTests/MHS/Build builds and runs a library with a hidden module and an executable using it, with the bounds of a package from Hackage, and is skipped when mhs is not on the PATH. MHS/Macros checks the version macros and is skipped unless cpphs, which mhs runs for CPP, is on the PATH too. MHS/CSources checks the C sources of a library built in place and of one installed in the store from a local repository, and MHS/InstallLib installs a library with cabal install --lib and compiles a program against it with mhs alone:

cabal run cabal-testsuite:cabal-tests -- --with-cabal="$(cabal list-bin cabal)" cabal-testsuite/PackageTests/MHS/Build/cabal.test.hs cabal-testsuite/PackageTests/MHS/Macros/cabal.test.hs cabal-testsuite/PackageTests/MHS/CSources/cabal.test.hs cabal-testsuite/PackageTests/MHS/InstallLib/cabal.test.hs

Part 2: MicroHs's own package list

MicroHs's Makefile.packages is the list of 91 Hackage packages its hackage-ci workflow installs with mcabal on every push. https://github.com/ulysses4ever/cabal-mhs-testsuite builds them with cabal and mhs instead, one at a time and in the same order, each into a store shared by all the steps, with this PR's commits applied; its CI also runs cabal-testsuite/PackageTests/MHS. Every step also runs a small executable depending on the package. As mhs compiles C sources when it links a program, even the executable that does nothing compiles the C sources of the package and its dependencies. For time, splitmix, random, QuickCheck, hspec, terminfo and binary, the executable calls into the package: QuickCheck passes 100 tests, hspec reports 1 example, 0 failures, and terminfo links with the curses library its configure script found. Where mcabal differs from cabal, the replay does what mcabal does, and its README says how: the --git overrides become source-repository-package stanzas, and since mcabal ignores version bounds, a few are lifted in project.extra, each with its reason. mhs has nothing installed besides base; cabal builds ghc-compat and the forks itself.

All the counts in the list of commits are for compiling each package as it is published, with no settings of its own: the project file sets no flags and no compiler options for any package of the list. It only chooses versions and sources: the index-state, the git repositories of MicroHs's Makefile, the three bounds that project.extra lifts and its one constraint, and a copy of hspec-core with its missing dependency added. The tiny executable of each step, which is not one of the packages, is linked with --no-as-needed on Linux.

The version macros take the count from 39 to 58: without them, transformers, which defines its Semigroup instances under #if MIN_VERSION_base(4,9,0), blocked 26 packages, and prettyprinter stopped at its own #error "MIN_VERSION_base macro not defined!". The rewrite takes it from 58 to 88: array from Hackage, which mhs cannot compile, blocked 17 of those 30 packages until array-mhs replaced it, and without ghc-compat, exceptions and call-stack failed on GHC.Stack, and time on Language.Haskell.TH.Syntax. The last three, time, binary and these, through binary, needed the headers of C sources, such as HsTime.h, which the commit for C sources puts on the include path.

One package needs a fix to build with cabal: hspec-core imports async without depending on it, as it compiles its vendored copy of async only for GHC. That works with mcabal because mhs sees every package that mcabal has installed. Cabal gives mhs only the declared dependencies, so the replay adds if impl(mhs) / build-depends: async to a copy of hspec-core, which is the fix that belongs upstream; without it, hspec-core and hspec above it fail.

Running the programs found problems outside cabal, in mhs and in some packages, which mcabal has too; INVENTORY.md lists them with reproductions.

Part 3: Building MicroHs itself (after patching it up)

MicroHs builds its base with mcabal build in lib. We allow cabal build it with mhs too, and the compiler from MicroHs.cabal, once four problems of those .cabal files that mcabal does not mind are fixed:

  1. library base has to be library,
  2. Data.Float is listed twice,
  3. six listed modules of base have no source,
  4. MicroHs.cabal lists for every compiler seven modules that only GHC needs.

microhs-cabal-fixes.patch fixes them, and MicroHs still builds with GHC with it. base also needs allow-boot-library-installs: True, as with any boot library.

The base that cabal builds has the same modules, exports and definitions as the one mcabal builds. cabal is no slower than the Makefile and mcabal, and, as it compresses programs as mcabal does, it builds an mhs of exactly the size of the Makefile's: the same machine code, and the same combinator code but for how 52 source locations of base are spelled. The testsuite's README has the times and sizes. An installation of mhs whose base and compiler cabal built passes the tests of cabal-testsuite/PackageTests/MHS and builds and runs the whole package list, and the testsuite's CI builds both on every run (CI run). cabal install exe:mhs does not give a working mhs, though: mhs looks for its packages and runtime only beside its binary, in the layout of its own installer.

INVENTORY.md lists every issue or wrinkle this work found outside cabal: in mhs, the MicroHs repository, MicroCabal, and some packages on Hackage, with reproductions.

Not covered yet

  • mhs-options in .cabal files, cabal repl, Backpack, foreign libraries, profiling and shared ways.
  • The replay runs a small program per package, but not the test suites of the packages. Running those with mhs is the next step there.
  • Upstream: hspec-core should depend on async for mhs, mhs could publish the versions at which its bundled packages should be presented, mhs could pass the libraries of packages after the C files, declare the C functions a program imports and expand $VERSION in mhs.conf to its version; MicroCabal could move to time 1.16.0.1, which fixed the time zone that time 1.15 gets wrong under mhs.

Checklist

QA Notes

A smoke test, and how to extend it in the direction you want to check.

Set up. Install MicroHs v0.16.9.0 from source, which also installs cpphs, the preprocessor that mhs runs for CPP and that the nixpkgs package lacks, and build cabal from this branch:

git clone --branch v0.16.9.0 https://github.com/augustss/MicroHs
make -C MicroHs -j all && make -C MicroHs minstall   # installs into ~/.mcabal; add ~/.mcabal/bin to PATH
cabal build cabal-install:exe:cabal
alias cabal-mhs="$(cabal list-bin cabal)"

Smoke test. A library, and an executable using it:

mkdir -p mhs-demo/src mhs-demo/app && cd mhs-demo
printf 'packages: .\ncompiler: mhs\nwith-compiler: mhs\n' > cabal.project
cat > mhs-demo.cabal <<'EOF'
cabal-version: 3.0
name:          mhs-demo
version:       0.1.0.0

library
  exposed-modules:  MyLib
  build-depends:    base ^>=4.19, bytestring
  hs-source-dirs:   src
  default-language: Haskell2010

executable mhs-demo
  main-is:          Main.hs
  build-depends:    base, mhs-demo
  hs-source-dirs:   app
  default-language: Haskell2010
EOF
cat > src/MyLib.hs <<'EOF'
module MyLib (greeting) where

import qualified Data.ByteString.Char8 as B

greeting :: String
greeting = B.unpack (B.pack "hello from mhs")
EOF
cat > app/Main.hs <<'EOF'
module Main (main) where

import MyLib (greeting)

main :: IO ()
main = putStrLn greeting
EOF
cabal-mhs run mhs-demo   # prints: hello from mhs

Expected: Build profile: -w mhs-0.16.9.0 -O1, and ghc-compat built from Hackage first, as every package depends on it with mhs; base ^>=4.19 and bytestring resolve to what the base of mhs provides. With -v2, the mhs commands show the library compiled into a package file, and the executable compiled with that file and base preloaded (-p) and the search path of mhs cleared (-a).

Extend it. The tests in cabal-testsuite/PackageTests/MHS do most of these:

  • A dependency from Hackage: add random to the library's build-depends, and cabal-mhs build builds the fork random-mhs instead.
  • CPP: add {-# LANGUAGE CPP #-} to MyLib, and choose code with #if MIN_VERSION_base(4,19,0) (MHS/Macros).
  • C sources: give the library c-sources: cbits/twice.c, include-dirs: include and install-includes: twice.h, and MyLib a foreign import ccall "twice.h twice" twice :: CInt -> CInt; mhs compiles the C file when it links the executable (MHS/CSources).
  • The store: cabal-mhs install exe:mhs-demo builds the package in the store and installs the executable.
  • Plain mhs: cabal-mhs install --lib installs the library where mcabal installs packages, into ~/.mcabal/mhs-0.16.9.0, which make minstall made the package directory of mhs itself, so that mhs Prog.hs compiles a program that imports MyLib (MHS/InstallLib).

🤖 Generated with Claude Code

https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj

@ulysses4ever

Copy link
Copy Markdown
Collaborator Author

I got as far as solving dependencies: on a freshly cabal inited package with cabal.project like this:

packages: .
compiler: mhs
with-compiler: mhs

I'm getting:

❯ cabal build
Warning: don't know how to find change monitoring files for the installed
package databases for mhs
Resolving dependencies...
Error: [Cabal-7107]
Could not resolve dependencies:
[__0] trying: mhs-test-0.1.0.0 (user goal)
[__1] next goal: base (dependency of mhs-test)
[__1] rejecting: base; 4.21.0.0, 4.20.2.0, 4.20.1.0, 4.20.0.1, 4.20.0.0, 4.19.2.0, 4.19.1.0, 4.19.0.0, 4.18.3.0, 4.18.2.1, 4.18.2.0, 4.18.1.0, 4.18.0.0, 4.17.2.1, 4.17.2.0, 4.17.1.0, 4.17.0.0, 4.16.4.0, 4.16.3.0, 4.16.2.0, 4.16.1.0, 4.16.0.0, 4.15.1.0, 4.15.0.0, 4.14.3.0, 4.14.2.0, 4.14.1.0, 4.14.0.0, 4.13.0.0, 4.12.0.0, 4.11.1.0, 4.11.0.0, 4.10.1.0, 4.10.0.0, 4.9.1.0, 4.9.0.0, 4.8.2.0, 4.8.1.0, 4.8.0.0, 4.7.0.2, 4.7.0.1, 4.7.0.0, 4.6.0.1, 4.6.0.0, 4.5.1.0, 4.5.0.0, 4.4.1.0, 4.4.0.0, 4.3.1.0, 4.3.0.0, 4.2.0.2, 4.2.0.1, 4.2.0.0, 4.1.0.0, 4.0.0.0, 3.0.3.2, 3.0.3.1 (constraint from non-reinstallable package requires installed instance)
[__1] fail (backjumping, conflict set: base, mhs-test)
After searching the rest of the dependency tree exhaustively, these were the goals I've had most trouble fulfilling: base, mhs-test

I guess I need to better understand how MHS treats base.

@ulysses4ever

Copy link
Copy Markdown
Collaborator Author

@augustss I hope to get your input here. This work (allowing cabal to drive mhs) uncovered many things some of which need fixing on mhs' side if you are willing to review. The full list is here: https://github.com/ulysses4ever/cabal-mhs-testsuite/blob/main/INVENTORY.md I can start submitting them to the mhs repo if this seems useful to you. Also, I'm happy to discuss them elsewhere if that's preferable.

@augustss

Copy link
Copy Markdown
Contributor

Let me look at the list.

@ulysses4ever
ulysses4ever marked this pull request as ready for review October 16, 2026 19:55
ulysses4ever and others added 6 commits October 16, 2026 18:33
Add a MicroHs backend to Cabal, Distribution.Simple.MHS, so that
cabal-install can build simple packages with mhs: a library with exposed and
other modules, and executables depending on it, in place or in the store.
Cabal knows mhs as a program, whose version is that of mhs --numeric-version,
and configure, build, install and register dispatch to the backend for the
compiler flavour MHS.

MicroHs compiles a library into a single package file, and finds installed
packages through a search path of directories with an index of the modules
they provide. Cabal maps its package databases onto this as follows (see
Note [MicroHs package databases] in Distribution.Simple.MHS):

* The global database is the default search path of mhs, reported by
  'mhs -L'; its packages (base, MicroHs) are described by 'mhs -L<pkgfile>'
  and get <name>-<version> as their unit id, which is what the solver and
  'configure' then refer to.
* A database of Cabal is a directory of <unit-id>.conf registration files;
  the package file of a library stays in its build directory or, once
  installed, in its library directory.
* A component is compiled with the package files of its dependency closure
  preloaded (-p) and the search path cleared (-a), so that it sees exactly
  the packages it depends on, and a library rebuilt in place is picked up
  by its dependents without re-registration.

Executables are linked by mhs into <builddir>/<exe>/<exe>, where cabal-install
expects them, so 'cabal run' and 'cabal list-bin' work. 'Setup configure'
gains a --mhs flag, the package databases are monitored for changes, and
'cabal path' knows the compiler program. The help texts of the commands in
doc/ are regenerated for --mhs and the mhs program.

Executables are compressed with -z, as MicroCabal does, so that the mhs that
cabal builds is the size of the one its Makefile builds.

Every extension is described as supported, as MicroCabal effectively does by
not checking them: mhs has the extensions it implements always on, CPP
excepted, and ignores the LANGUAGE pragmas of the others. Describing only
the extensions it implements made Cabal reject packages for extensions they
merely declare, often for the GHC side of a conditional: Trustworthy
(parsec), DeriveGeneric (pretty), UnboxedTuples (unordered-containers,
vault), TypeFamilies (newtype, contravariant, assoc), or
NoGeneralizedNewtypeDeriving, for which the solver fell back from time-1.15,
which supports mhs, to time-1.12.2. A package that needs an extension mhs
lacks fails when mhs meets the syntax. reexported-modules are accepted too:
mhs sees all the modules of the packages it loads, which include the
dependencies of the dependencies of a component, so the modules a package
re-exports are visible to the components depending on it, as long as they
are not renamed. The errors of the backend are defined in
Distribution.Simple.MHS too.

Tested with mhs 0.16.9.0 and 0.15.4.0: cabal build/run/list-bin of a library
with a hidden module plus an executable, an up-to-date rebuild, a rebuild
after editing the hidden module, and 'cabal install' of the executable
through the store. A cabal-testsuite test covers the first of these and is
skipped when mhs is not installed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
The base of mhs is versioned like the compiler (base-0.16.9.0) and bundles
the modules of packages that are separate on Hackage: bytestring, text,
deepseq, stm, directory, process, hashable and integer-logarithms. A package
written for GHC depends on base-4.x and on those packages by name, so it
cannot be solved against what mhs provides: the only installed base is
0.16.9.0, and base is not reinstallable.

Describe the base of mhs the way MicroCabal does, with the versions from its
builtinPackages table ("totally fake; they are from GHC"): base at 4.19.1.0,
and the bundled packages as installed packages of their own, at the versions
MicroCabal gives them, depending on base, with the modules that are theirs
and no package file. The unit id of base stays base-0.16.9.0, which is also
the name of its package file, so the dependency closure of a component still
preloads the one base.pkg. A package file is now looked up under the unit id
first, then under the munged package id of a library built by Cabal, and a
package without library directories has nothing to load.

Only hashable gets a version of its own. MicroCabal has it at 1.0.0.0, "very
rudimentary", which makes no difference to it, as it does not check bounds.
But the Data.Hashable of mhs has the API of the 1.4 series
(Data.Hashable.Lifted, Hashable1 with an Eq1 superclass), and the packages
that depend on hashable require at least that: unordered-containers 1.4,
these 1.4.4. With 1.0.0.0, the solver built hashable-1.5.1.0 from Hackage for
them instead, which mhs cannot compile, so hashable is presented at 1.4.7.0.

Tested with mhs 0.16.9.0: a library with 'base ^>=4.19, bytestring >=0.11'
and an executable with 'base >=4.14 && <5' solve, build and run, with only
base.pkg preloaded; the testsuite package has such bounds now.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
GHC includes cabal_macros.h with -optP-include. mhs passes on to cpphs only
the -D and -I options it is given, so give it the macros of the header one
by one, as MicroCabal does. Distribution.Simple.MHS turns every #define of
the header that generateCabalMacrosHeader renders, which is unchanged, into
a -D option: VERSION_<pkg> and MIN_VERSION_<pkg>(major1,major2,minor) for the
package and its dependencies, the TOOL_ variants for the configured
programs, and the CURRENT_ constants, the first definition of a macro
winning as in the header. The autogen
directory of the component is on the include path of cpphs too, so a module
that includes cabal_macros.h itself finds it.

Many packages choose code on these macros, transformers for instance its
Semigroup instances, and without them took the branch for an ancient base.

The test of mhs moves to PackageTests/MHS/Build, and PackageTests/MHS/Macros
checks the macros; it is skipped unless mhs and its cpphs are installed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
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
mhs compiles no C when it builds a library. It compiles the C sources of a
library when it links a program that loads the package file of the library,
from <name>-<version>/cbits beside the package file, which is where
MicroCabal installs the c-sources of a library. Cabal ignored the C sources
of libraries with a warning, and gave mhs no include directories of the
dependencies of a component, so a program calling into the C code of a
library failed to compile, as with time, which takes the time zone from C:

  fatal error: HsTime.h: No such file or directory

buildLib now copies the c-sources of a library there, beside the package
file in its build directory, and installLib copies them along with the
package file. The headers are found as with GHC: the include-dirs that the
registrations of the dependencies of a component list, which is where Cabal
installs their install-includes for every compiler, are given to mhs with
-I, which it passes on to the C compiler. See Note [MicroHs C files of
libraries]. The cc-options of an executable are now passed to mhs, with
-optc, for its own C sources.

The test MHS/CSources builds a library with C sources and a header from a
repository, into the store, and another one in place, and runs an
executable calling both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj
mhs has no package environments: it finds packages in the package
directories of its search path, into which 'mhs -Q' installs them. With
mhs, cabal install --lib crashed, as it asked about the package
environments of a compiler other than GHC:

  Distribution.Simple.GHC.Props.getImplProps only worksfor GHC-like
  compilers (GHC, GHCJS), but found MHS

It now installs the libraries, with the libraries they depend on that mhs
does not find already, into a package directory of mhs with 'mhs -Q', and
the C sources and headers of each beside it, where mhs looks for them. The
directory is UserPackageDB, where MicroCabal installs packages,
~/.mcabal/mhs-<version> or $CABALDIR/mhs-<version>, unless another one is
given with --package-env. After 'make minstall', ~/.mcabal/mhs-<version> is
the package directory of mhs itself, so that mhs finds the libraries there
as it finds those of MicroCabal. The package directory of the user that the
configuration of mhs names, ~/.mcabal-user/mhs-<version>, would not do:
MicroHs 0.16.9.0 does not search it, as it expands $VERSION in its
configuration to its own directory instead of its version. cabal says
whether mhs searches the directory or has to be given it with -a, as it has
to when mhs is installed elsewhere; mhs names the directories of its search
path as it reaches them, such as /path/to/bin/../mhs-0.16.9.0, so they are
compared canonicalized.

The test MHS/InstallLib installs a library with a C source where MicroCabal
installs and compiles a program using it with plain mhs.

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Unresolved backend correctness issues can break supported package builds and leave installed-package information stale.

6 open issues
What changed in this PR

Adds MicroHs support to Cabal’s compiler backend and cabal-install’s planning and installation workflows.

Changes:

  • Implements MHS configuration, package discovery, building, registration, and installation.
  • Adapts dependency solving and library installation for MicroHs.
  • Adds integration fixtures, documentation, and generated help updates.
File Description
doc/​setup-help/​configure.txt Documents MHS compiler selection.
doc/​setup-help/​build.txt Lists the MHS program.
doc/​setup-commands.rst Documents MHS options.
doc/​config.rst Lists MicroHs among known programs.
doc/​cmd-v2-help/​update.txt Adds MHS selection help.
doc/​cmd-v2-help/​test.txt Adds MHS selection help.
doc/​cmd-v2-help/​target.txt Adds MHS selection help.
doc/​cmd-v2-help/​run.txt Adds MHS selection help.
doc/​cmd-v2-help/​repl.txt Adds MHS selection help.
doc/​cmd-v2-help/​path.txt Adds MHS selection help.
doc/​cmd-v2-help/​outdated.txt Adds MHS selection help.
doc/​cmd-v2-help/​list-bin.txt Adds MHS selection help.
doc/​cmd-v2-help/​install.txt Adds MHS selection help.
doc/​cmd-v2-help/​haddock.txt Adds MHS selection help.
doc/​cmd-v2-help/​gen-bounds.txt Adds MHS selection help.
doc/​cmd-v2-help/​freeze.txt Adds MHS selection help.
doc/​cmd-v2-help/​exec.txt Adds MHS selection help.
doc/​cmd-v2-help/​configure.txt Adds MHS selection help.
doc/​cmd-v2-help/​build.txt Adds MHS selection help.
doc/​cmd-v2-help/​bench.txt Adds MHS selection help.
doc/​cmd-v1-help/​reconfigure.txt Adds MHS selection help.
doc/​cmd-v1-help/​install.txt Documents MHS selection and program.
doc/​cmd-v1-help/​configure.txt Documents MHS selection and program.
doc/​cmd-v1-help/​build.txt Lists the MHS program.
doc/​cabal-project-description-file.rst Lists MHS compiler configuration.
doc/​cabal-package-description-file.rst Updates documented compiler flavors.
doc/​cabal-commands.rst Explains MHS library installation.
changelog.d/​pr-11392.md Records support and limitations.
Cabal/​src/​Distribution/​Simple/​Setup/​Config.hs Adds the --mhs option.
Cabal/​src/​Distribution/​Simple/​Register.hs Dispatches MHS registration operations.
Cabal/​src/​Distribution/​Simple/​Program/​Builtin.hs Defines the MHS program.
Cabal/​src/​Distribution/​Simple/​Program.hs Exports the MHS program.
Cabal/​src/​Distribution/​Simple/​MHS.hs Implements the MicroHs backend.
Cabal/​src/​Distribution/​Simple/​Install.hs Dispatches MHS installation.
Cabal/​src/​Distribution/​Simple/​Configure.hs Integrates MHS configuration and discovery.
Cabal/​src/​Distribution/​Simple/​Compiler.hs Generalizes reexport capability detection.
Cabal/​src/​Distribution/​Simple/​Build.hs Dispatches MHS builds.
Cabal/​Cabal.cabal Exposes the backend module.
cabal-testsuite/​PackageTests/​MHS/​Macros/​src/​Versions.hs Exercises version macros.
cabal-testsuite/​PackageTests/​MHS/​Macros/​mhs-macros.cabal Defines the macro fixture.
cabal-testsuite/​PackageTests/​MHS/​Macros/​cabal.test.hs Checks macro output.
cabal-testsuite/​PackageTests/​MHS/​Macros/​cabal.project Selects MHS for macro tests.
cabal-testsuite/​PackageTests/​MHS/​Macros/​app/​Main.hs Exercises header and library macros.
cabal-testsuite/​PackageTests/​MHS/​InstallLib/​src/​Triple.hs Provides an FFI library fixture.
cabal-testsuite/​PackageTests/​MHS/​InstallLib/​prog/​Prog.hs Consumes the installed library.
cabal-testsuite/​PackageTests/​MHS/​InstallLib/​mhs-installlib.cabal Defines the installation fixture.
cabal-testsuite/​PackageTests/​MHS/​InstallLib/​include/​triple.h Supplies the installed C header.
cabal-testsuite/​PackageTests/​MHS/​InstallLib/​cbits/​triple.c Implements the C fixture.
cabal-testsuite/​PackageTests/​MHS/​InstallLib/​cabal.test.hs Tests installation and standalone consumption.
cabal-testsuite/​PackageTests/​MHS/​InstallLib/​cabal.project Selects MHS for installation tests.
cabal-testsuite/​PackageTests/​MHS/​CSources/​src/​Twice.hs Exercises in-place library FFI.
cabal-testsuite/​PackageTests/​MHS/​CSources/​repo/​mhs-cdep-1.0/​src/​Add.hs Exercises dependency C API imports.
cabal-testsuite/​PackageTests/​MHS/​CSources/​repo/​mhs-cdep-1.0/​mhs-cdep.cabal Defines the repository dependency.
cabal-testsuite/​PackageTests/​MHS/​CSources/​repo/​mhs-cdep-1.0/​include/​add.h Supplies dependency declarations and macro.
cabal-testsuite/​PackageTests/​MHS/​CSources/​repo/​mhs-cdep-1.0/​cbits/​add.c Implements dependency C functionality.
cabal-testsuite/​PackageTests/​MHS/​CSources/​mhs-csources.cabal Defines C compilation fixtures.
cabal-testsuite/​PackageTests/​MHS/​CSources/​include/​twice.h Supplies the library header.
cabal-testsuite/​PackageTests/​MHS/​CSources/​cbits/​twice.c Implements library C functionality.
cabal-testsuite/​PackageTests/​MHS/​CSources/​cabal.test.hs Checks C linking and compiler options.
cabal-testsuite/​PackageTests/​MHS/​CSources/​cabal.project Selects MHS for C tests.
cabal-testsuite/​PackageTests/​MHS/​CSources/​app/​offset.h Declares executable C functionality.
cabal-testsuite/​PackageTests/​MHS/​CSources/​app/​offset.c Exercises a C compiler definition.
cabal-testsuite/​PackageTests/​MHS/​CSources/​app/​Main.hs Runs the C integration checks.
cabal-testsuite/​PackageTests/​MHS/​Build/​src/​Greeting/​Internal.hs Exercises hidden modules and bytestring.
cabal-testsuite/​PackageTests/​MHS/​Build/​src/​Greeting.hs Exposes the library greeting.
cabal-testsuite/​PackageTests/​MHS/​Build/​mhs-test.cabal Defines the build fixture.
cabal-testsuite/​PackageTests/​MHS/​Build/​cabal.test.hs Checks building and running.
cabal-testsuite/​PackageTests/​MHS/​Build/​cabal.project Selects MHS for build tests.
cabal-testsuite/​PackageTests/​MHS/​Build/​app/​Main.hs Consumes the fixture library.
cabal-install/​src/​Distribution/​Client/​ProjectPlanning.hs Applies compiler-specific dependency rewriting.
cabal-install/​src/​Distribution/​Client/​Dependency.hs Rewrites MHS source-package descriptions.
cabal-install/​src/​Distribution/​Client/​CmdPath.hs Resolves the MHS compiler program.
cabal-install/​src/​Distribution/​Client/​CmdInstall.hs Routes MHS library installation.

Review effort: Balanced

-- include the dependencies of the dependencies of a component, so
-- the modules a package re-exports are visible to the components
-- depending on it, as long as they are not renamed.
Map.fromList [("Support reexported-modules", "YES")]
Comment on lines +252 to +253
dependsOnGhcCompat =
any ((== ghcCompat) . depPkgName) (allBuildDepends (flattenPackageDescription gpd0))
-> Maybe (SymbolicPath CWD (Dir from))
-> PackageDBStackX (SymbolicPath from (Dir PkgDB))
-> IO [FilePath]
getInstalledPackagesMonitorFiles = packageDbDirs
Comment on lines +568 to +569
for_ sources $ \source ->
installOrdinaryFile verbosity source (dir </> takeFileName source)
targetDir = buildDir lbi </> makeRelativePathEx exeName'
exeFile = targetDir </> makeRelativePathEx (exeName' <.> exeExtension (hostPlatform lbi))
u = interpretSymbolicPathCWD -- See Note [Symbolic paths] in Distribution.Utils.Path
srcMainPath <- findFileCwd verbosity (mbWorkDirLBI lbi) (sourceDirs bi) (modulePath exe)
Comment on lines +615 to +617
++ ["-i" ++ u dir | dir <- sourceDirs bi]
++ ["-i" ++ u (autogenComponentModulesDir lbi clbi)]
++ ["-i" ++ u (autogenPackageModulesDir lbi)]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants