Repository navigation
MicroHS support - #11392
MicroHS support#11392ulysses4ever wants to merge 6 commits into
Conversation
|
I got as far as solving dependencies: on a freshly I'm getting: I guess I need to better understand how MHS treats |
dcb78df to
1f2da2d
Compare
1f2da2d to
b9bb698
Compare
|
@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. |
|
Let me look at the list. |
b9bb698 to
eaf6002
Compare
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
eaf6002 to
5bcc71f
Compare
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved backend correctness issues can break supported package builds and leave installed-package information stale.
6 open issues
Reject unsupported renamed module reexports · New Inject ghc-compat independently into each component · New Monitor package files and subdirectories for index changes · New Preserve private headers and relative include layout · New Search preprocessing output for generated main-is files · New Include preprocessing output in module search paths · New
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")] |
| dependsOnGhcCompat = | ||
| any ((== ghcCompat) . depPkgName) (allBuildDepends (flattenPackageDescription gpd0)) |
| -> Maybe (SymbolicPath CWD (Dir from)) | ||
| -> PackageDBStackX (SymbolicPath from (Dir PkgDB)) | ||
| -> IO [FilePath] | ||
| getInstalledPackagesMonitorFiles = packageDbDirs |
| 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) |
| ++ ["-i" ++ u dir | dir <- sourceDirs bi] | ||
| ++ ["-i" ++ u (autogenComponentModulesDir lbi clbi)] | ||
| ++ ["-i" ++ u (autogenPackageModulesDir lbi)] |

What's achieved
lib:Cabalandcabal-installcan 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.hsmodule and tests.cabal-installMHS.hsDistribution.Simple.MHS(MHS.hs)cabal-testsuite(Counted with
git diff --numstatagainstmasterataeac769.)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.hsare 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
--mhsand themhsprogram.Commit history
Each commit ends with how many of the 91 packages of MicroHs's own list build, and run a program, with
cabalat that commit, replayed for every commit in the same way, with nothing installed for mhs besides itsbase(see Testing below and all results in a separate repo).2e55647Adds themhsprogram and the backendDistribution.Simple.MHS, to which configure, build, install and register dispatch: it builds libraries into mhs package files and executables against them, compressed with-zasmcabaldoes, and installs and registers both, in place and in the store; the packages of mhs itself are described withmhs -L. Every extension is accepted, as mhs ignores the pragmas of those it lacks, and so arereexported-modules, as mhs sees every module of the packages it loads. 3/91 build and run: withbaseat 0.16.9.0, the solver rejects most packages from Hackage, or falls back to ancient versions of them, until the next commit.1b449d1Presents thebaseof mhs at version 4.19.1.0, and the packages it bundles, such asbytestringandtext, as installed packages at the versions MicroCabal uses, excepthashable, at 1.4.7.0, the API that mhs implements, rather than MicroCabal's 1.0.0.0. 39/91 build and run.6528204Passes the macros ofcabal_macros.h, such asMIN_VERSION_base, to the preprocessor of mhs as-Doptions. 58/91 build and run.b289e8fRewrites, incabal-install, the packages that the solver sees, as MicroCabal does:ghc-compat, which has the modules that are part ofbasewith GHC, becomes a dependency of every component, andarrayandrandombecome their forksarray-mhsandrandom-mhs, so thatcabalplans and builds all three from Hackage, and mhs needs nothing installed besidesbase. Rewriting what the solver sees is enough becausecabal-installnow builds packages in-library (#11703): it configures each package with the description in its plan, which is rewritten already, rather than running aSetupscript that would read the original.cabalfile again. 88/91 build and run.cbfde3dPuts 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.5bcc71fcabal install --libinstalls libraries and their dependencies withmhs -Qinto the directory wheremcabalinstalls packages, which is the package directory of mhs itself aftermake minstall, or into the one given with--package-env, so that plainmhsfinds them, rather than writing a GHC environment file. 91/91 build and run.Testing: 3 parts
Part 1:
cabal-testsuitecabal-testsuite/PackageTests/MHS/Buildbuilds and runs a library with a hidden module and an executable using it, with the bounds of a package from Hackage, and is skipped whenmhsis not on the PATH.MHS/Macroschecks the version macros and is skipped unlesscpphs, which mhs runs for CPP, is on the PATH too.MHS/CSourceschecks the C sources of a library built in place and of one installed in the store from a local repository, andMHS/InstallLibinstalls a library withcabal install --liband compiles a program against it with mhs alone:Part 2: MicroHs's own package list
MicroHs's
Makefile.packagesis the list of 91 Hackage packages itshackage-ciworkflow installs withmcabalon every push. https://github.com/ulysses4ever/cabal-mhs-testsuite builds them withcabaland 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 runscabal-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. Fortime,splitmix,random,QuickCheck,hspec,terminfoandbinary, the executable calls into the package: QuickCheck passes 100 tests, hspec reports1 example, 0 failures, andterminfolinks with the curses library its configure script found. Wheremcabaldiffers fromcabal, the replay does whatmcabaldoes, and its README says how: the--gitoverrides becomesource-repository-packagestanzas, and sincemcabalignores version bounds, a few are lifted inproject.extra, each with its reason. mhs has nothing installed besidesbase;cabalbuildsghc-compatand 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 thatproject.extralifts and its one constraint, and a copy ofhspec-corewith its missing dependency added. The tiny executable of each step, which is not one of the packages, is linked with--no-as-neededon Linux.The version macros take the count from 39 to 58: without them, transformers, which defines its
Semigroupinstances 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:arrayfrom Hackage, which mhs cannot compile, blocked 17 of those 30 packages untilarray-mhsreplaced it, and withoutghc-compat, exceptions and call-stack failed onGHC.Stack, and time onLanguage.Haskell.TH.Syntax. The last three, time, binary and these, through binary, needed the headers of C sources, such asHsTime.h, which the commit for C sources puts on the include path.One package needs a fix to build with
cabal: hspec-core importsasyncwithout depending on it, as it compiles its vendored copy of async only for GHC. That works withmcabalbecause mhs sees every package thatmcabalhas installed. Cabal gives mhs only the declared dependencies, so the replay addsif impl(mhs)/build-depends: asyncto 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, whichmcabalhas too;INVENTORY.mdlists them with reproductions.Part 3: Building MicroHs itself (after patching it up)
MicroHs builds its
basewithmcabal buildinlib. We allowcabalbuild it withmhstoo, and the compiler fromMicroHs.cabal, once four problems of those.cabalfiles thatmcabaldoes not mind are fixed:library basehas to belibrary,Data.Floatis listed twice,basehave no source,MicroHs.caballists for every compiler seven modules that only GHC needs.microhs-cabal-fixes.patchfixes them, and MicroHs still builds with GHC with it.basealso needsallow-boot-library-installs: True, as with any boot library.The
basethatcabalbuilds has the same modules, exports and definitions as the onemcabalbuilds.cabalis no slower than the Makefile andmcabal, and, as it compresses programs asmcabaldoes, it builds anmhsof exactly the size of the Makefile's: the same machine code, and the same combinator code but for how 52 source locations ofbaseare spelled. The testsuite's README has the times and sizes. An installation of mhs whosebaseand compilercabalbuilt passes the tests ofcabal-testsuite/PackageTests/MHSand builds and runs the whole package list, and the testsuite's CI builds both on every run (CI run).cabal install exe:mhsdoes 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.mdlists every issue or wrinkle this work found outsidecabal: in mhs, the MicroHs repository, MicroCabal, and some packages on Hackage, with reproductions.Not covered yet
mhs-optionsin.cabalfiles,cabal repl, Backpack, foreign libraries, profiling and shared ways.hspec-coreshould depend onasyncfor 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$VERSIONin 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
significance: significantin the changelog file.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 buildcabalfrom this branch:Smoke test. A library, and an executable using it:
Expected:
Build profile: -w mhs-0.16.9.0 -O1, andghc-compatbuilt from Hackage first, as every package depends on it with mhs;base ^>=4.19andbytestringresolve to what thebaseof mhs provides. With-v2, themhscommands show the library compiled into a package file, and the executable compiled with that file andbasepreloaded (-p) and the search path of mhs cleared (-a).Extend it. The tests in
cabal-testsuite/PackageTests/MHSdo most of these:randomto the library'sbuild-depends, andcabal-mhs buildbuilds the forkrandom-mhsinstead.{-# LANGUAGE CPP #-}toMyLib, and choose code with#if MIN_VERSION_base(4,19,0)(MHS/Macros).c-sources: cbits/twice.c,include-dirs: includeandinstall-includes: twice.h, andMyLibaforeign import ccall "twice.h twice" twice :: CInt -> CInt; mhs compiles the C file when it links the executable (MHS/CSources).cabal-mhs install exe:mhs-demobuilds the package in the store and installs the executable.cabal-mhs install --libinstalls the library wheremcabalinstalls packages, into~/.mcabal/mhs-0.16.9.0, whichmake minstallmade the package directory of mhs itself, so thatmhs Prog.hscompiles a program that importsMyLib(MHS/InstallLib).🤖 Generated with Claude Code
https://claude.ai/code/session_0176vAefrw8jPKXdh9h1wKuj