How does cabal-install choose boot library versions?

I always thought that cabal-install chose the latest version it could of any dependency, but this seems not to be the case, at least perhaps not for boot libraries.

Does anyone know what its actual behaviour is supposed to be? For example if I have a package that only depends on Cabal:

cabal-version:      3.0
name:               tmp-Cabal
version:            0

executable tmp-Cabal
    main-is:          Main.hs
    build-depends:    base,
                      Cabal,
                      -- Cabal == 3.8.1.0,
                      -- Cabal == 3.10.3.0,
    hs-source-dirs:   app

then when built with GHC 9.4 with no version bounds supplied,
cabal-install choses 3.8, the boot library version for 9.4:

% cabal build --enable-shared --enable-executable-dynamic -w ghc-9.4.8 && ldd dist-newstyle/build/x86_64-linux/ghc-9.4.8/tmp-Cabal-0/x/tmp-Cabal/build/tmp-Cabal/tmp-Cabal | grep -i cabal              
Up to date
        libHSCabal-3.8.1.0-ghc9.4.8.so => /home/tom/.ghcup/ghc/9.4.8/lib/ghc-9.4.8/lib/x86_64-linux-ghc-9.4.8/libHSCabal-3.8.1.0-ghc9.4.8.so (0x00007fa2b99fb000)
        libHSCabal-syntax-3.8.1.0-ghc9.4.8.so => /home/tom/.ghcup/ghc/9.4.8/lib/ghc-9.4.8/lib/x86_64-linux-ghc-9.4.8/libHSCabal-syntax-3.8.1.0-ghc9.4.8.so (0x00007fa2b8a42000)

But there is no actual problem picking a later version. For example, I can force it to pick 3.10 by changing the .cabal file:

% cabal build --enable-shared --enable-executable-dynamic -w ghc-9.4.8 && ldd dist-newstyle/build/x86_64-linux/ghc-9.4.8/tmp-Cabal-0/x/tmp-Cabal/build/tmp-Cabal/tmp-Cabal | grep -i cabal
Up to date
        libHSCabal-3.10.3.0-82a9ca158ac4eadab7ceaf13ead72179c5fe92a217580b5650d33641d078adfa-ghc9.4.8.so => /home/tom/.cabal/store/ghc-9.4.8/Cabal-3.10.3.0-82a9ca158ac4eadab7ceaf13ead72179c5fe92a217580b5650d33641d078adfa/lib/libHSCabal-3.10.3.0-82a9ca158ac4eadab7ceaf13ead72179c5fe92a217580b5650d33641d078adfa-ghc9.4.8.so (0x00007f1123e4b000)
        libHSCabal-syntax-3.10.3.0-dd6fb5bf6d9bb60d6766f4e73dfaaf692f987a198cefc19200c8f20e2d32da6c-ghc9.4.8.so => /home/tom/.cabal/store/ghc-9.4.8/Cabal-syntax-3.10.3.0-dd6fb5bf6d9bb60d6766f4e73dfaaf692f987a198cefc19200c8f20e2d32da6c/lib/libHSCabal-syntax-3.10.3.0-dd6fb5bf6d9bb60d6766f4e73dfaaf692f987a198cefc19200c8f20e2d32da6c-ghc9.4.8.so (0x00007f1122bf7000)

So does cabal-install pick the latest version of non-boot dependencies, and for boot dependencies it installs the default version if it can? If so, why?


This came up at Switch to Cabal-syntax; widen compatibility range by ulidtko · Pull Request #112 · kowainik/extensions · GitHub

2 Likes

See these issues: Tracking issue: easily reinstallable boot libraries · Issue #10440 · haskell/cabal · GitHub

The current behaviour is that for boot libraries, cabal will always prefer the version that came with GHC unless it’s impossible to create a build plan with that version.

And for some boot libraries, it won’t allow reinstalling even then.

7 Likes

That solves it, thanks!

1 Like

A more focused discussion of the issue is here: Special treatment of pre-installed packages by the solver · Issue #9669 · haskell/cabal · GitHub It is a questionable design. I think the idea was that picking boot versions will always result in at most the same amount of work (i.e. build time) because boot libraries (binaries) are always available, while any other version may require building. But certainly there are different metrics that can be made into the optimization target, e.g., my favourite, the least-surprise principle. If we optimize for build time, it’s unclear why not try to pick an already-built version for any package…

3 Likes