From 896b67e1e36df3fe9f4cb2a6ba07e608b4c7b0fa Mon Sep 17 00:00:00 2001 From: sorki Date: Fri, 1 May 2026 09:36:20 +0200 Subject: [PATCH 1/9] cabal-install-solver: derive couple more Show instances --- .../src/Distribution/Solver/Modular/Dependency.hs | 6 +++++- .../src/Distribution/Solver/Modular/Index.hs | 1 + .../src/Distribution/Solver/Modular/Validate.hs | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Dependency.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Dependency.hs index fff2dacde4e..3b0fe5a95eb 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Dependency.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Dependency.hs @@ -1,5 +1,6 @@ {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE RecordWildCards #-} +{-# LANGUAGE StandaloneDeriving #-} module Distribution.Solver.Modular.Dependency ( -- * Variables Var(..) @@ -94,6 +95,8 @@ data FlaggedDep qpn = -- | Dependencies which are always enabled, for the component 'comp'. | Simple (LDep qpn) Component +deriving instance Show qpn => Show (FlaggedDep qpn) + -- | Conservatively flatten out flagged dependencies -- -- NOTE: We do not filter out duplicates. @@ -115,6 +118,7 @@ type FalseFlaggedDeps qpn = FlaggedDeps qpn -- depending; having a 'Functor' instance makes bugs where we don't distinguish -- these two far too likely. (By rights 'LDep' ought to have two type variables.) data LDep qpn = LDep (DependencyReason qpn) (Dep qpn) + deriving Show -- | A dependency (constraint) associates a package name with a constrained -- instance. It can also represent other types of dependencies, such as @@ -123,7 +127,7 @@ data Dep qpn = Dep (PkgComponent qpn) CI -- ^ dependency on a package component | Ext Extension -- ^ dependency on a language extension | Lang Language -- ^ dependency on a language version | Pkg PkgconfigName PkgconfigVersionRange -- ^ dependency on a pkg-config package - deriving Functor + deriving (Functor, Show) -- | An exposed component within a package. This type is used to represent -- build-depends and build-tool-depends dependencies. diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Index.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Index.hs index 2f28d12de85..e04b62dd0c7 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Index.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Index.hs @@ -36,6 +36,7 @@ data PInfo = PInfo (FlaggedDeps PN) (Map ExposedComponent ComponentInfo) FlagInfo (Maybe FailReason) + deriving Show -- | Info associated with each library and executable in a package instance. data ComponentInfo = ComponentInfo { diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Validate.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Validate.hs index ed01234bdba..88d494d4396 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Validate.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Validate.hs @@ -129,6 +129,7 @@ type PPreAssignment = Map QPN MergedPkgDep -- | A dependency on a component, including its DependencyReason. data PkgDep = PkgDep (DependencyReason QPN) (PkgComponent QPN) CI + deriving Show -- | Map from component name to one of the reasons that the component is -- required. @@ -146,6 +147,7 @@ type ComponentDependencyReasons = Map ExposedComponent (DependencyReason QPN) data MergedPkgDep = MergedDepFixed ExposedComponent (DependencyReason QPN) I | MergedDepConstrained [VROrigin] + deriving Show -- | Version ranges paired with origins. type VROrigin = (VR, ExposedComponent, DependencyReason QPN) From 9988d28389dd686893d9fe72ab3d9e729d02177e Mon Sep 17 00:00:00 2001 From: sorki Date: Fri, 1 May 2026 09:45:56 +0200 Subject: [PATCH 2/9] Group installed packages Before this patch, sub-libraries (either public or private) of installed packages are treated as top level separate packages from solvers point of view. This differs from how source packages are handled, which are treated as a single package. Sub-libraries of installed packages needed munging to prevent conflicts like described in `Note [Index conversion with internal libraries]`. Due to package name munging, top level library we are trying to build, cannot depend on public sub-libraries, more precisely - it can, but the installed packages are never used. Instead they are always rebuilt from source (as only source package index exposes public sub-libs correctly). The idea is to make packages coming from *installed* package index look similar to packages coming from *source* - appearing as a single package instead of multiple. The approach we choose is adding a grouping pass to solvers installed package index conversion. After the index is converted, we group installed packages by name and version and adjust all dependencies to point to newly built groups instead of split sub-lib packages. This way, solver only sees a single package (similar to how source packages are treated). This is done by means of adding * `InstGroup` `Loc` to solver * `installedSublibs` field to `InstalledPackageInfo` * and finally expanding `installedSublibs` in `ProjectPlanning` Now the sub-libs are handled correctly and build process picks installed public sub-libs when available. It also allows us to drop package name munging hacks from solver completely. Closes #6039 --- .../Types/InstalledPackageInfo.hs | 2 + .../InstalledPackageInfo/FieldGrammar.hs | 1 + .../tests/ParserTests/ipi/Includes2.expr | 1 + .../ipi/internal-preprocessor-test.expr | 1 + .../ParserTests/ipi/issue-2276-ghc-9885.expr | 1 + .../tests/ParserTests/ipi/transformers.expr | 1 + .../Distribution/Utils/Structured.hs | 2 +- Cabal/src/Distribution/Simple/Register.hs | 1 + .../Solver/Modular/ConfiguredConversion.hs | 30 ++++-- .../Solver/Modular/IndexConversion.hs | 98 +++++++++++++++++-- .../Distribution/Solver/Modular/Message.hs | 4 +- .../Distribution/Solver/Modular/Package.hs | 42 ++++++-- .../Distribution/Client/ProjectPlanning.hs | 32 +++--- .../Distribution/Solver/Modular/Solver.hs | 2 +- 14 files changed, 177 insertions(+), 41 deletions(-) diff --git a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs index b510a34d540..aa5d87666dc 100644 --- a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs +++ b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs @@ -42,6 +42,7 @@ data InstalledPackageInfo = InstalledPackageInfo sourcePackageId :: PackageId , sourceLibName :: LibraryName , installedComponentId_ :: ComponentId + , installedSublibs :: [InstalledPackageInfo] , libVisibility :: LibraryVisibility , installedUnitId :: UnitId , -- INVARIANT: if this package is definite, OpenModule's @@ -135,6 +136,7 @@ emptyInstalledPackageInfo = , sourceLibName = LMainLibName , installedComponentId_ = mkComponentId "" , installedUnitId = mkUnitId "" + , installedSublibs = mempty , instantiatedWith = [] , compatPackageKey = "" , license = Left SPDX.NONE diff --git a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs index 6d2849c5142..56f2381559b 100644 --- a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs +++ b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs @@ -134,6 +134,7 @@ ipiFieldGrammar = (PackageIdentifier pn _basicVersion) (combineLibraryName ln _basicLibName) (mkComponentId "") -- installedComponentId_, not in use + mempty -- installedSublibs _basicLibVisibility where MungedPackageName pn ln = _basicName diff --git a/Cabal-tests/tests/ParserTests/ipi/Includes2.expr b/Cabal-tests/tests/ParserTests/ipi/Includes2.expr index cc002958c05..4459fffc7f8 100644 --- a/Cabal-tests/tests/ParserTests/ipi/Includes2.expr +++ b/Cabal-tests/tests/ParserTests/ipi/Includes2.expr @@ -9,6 +9,7 @@ InstalledPackageInfo { (UnqualComponentName "mylib"), installedComponentId_ = ComponentId "", + installedSublibs = [], libVisibility = LibraryVisibilityPrivate, installedUnitId = UnitId diff --git a/Cabal-tests/tests/ParserTests/ipi/internal-preprocessor-test.expr b/Cabal-tests/tests/ParserTests/ipi/internal-preprocessor-test.expr index 3c3be058152..a0a9e419216 100644 --- a/Cabal-tests/tests/ParserTests/ipi/internal-preprocessor-test.expr +++ b/Cabal-tests/tests/ParserTests/ipi/internal-preprocessor-test.expr @@ -8,6 +8,7 @@ InstalledPackageInfo { sourceLibName = LMainLibName, installedComponentId_ = ComponentId "", + installedSublibs = [], libVisibility = LibraryVisibilityPublic, installedUnitId = UnitId diff --git a/Cabal-tests/tests/ParserTests/ipi/issue-2276-ghc-9885.expr b/Cabal-tests/tests/ParserTests/ipi/issue-2276-ghc-9885.expr index d5da47695e5..a652d1dcf09 100644 --- a/Cabal-tests/tests/ParserTests/ipi/issue-2276-ghc-9885.expr +++ b/Cabal-tests/tests/ParserTests/ipi/issue-2276-ghc-9885.expr @@ -8,6 +8,7 @@ InstalledPackageInfo { sourceLibName = LMainLibName, installedComponentId_ = ComponentId "", + installedSublibs = [], libVisibility = LibraryVisibilityPublic, installedUnitId = UnitId diff --git a/Cabal-tests/tests/ParserTests/ipi/transformers.expr b/Cabal-tests/tests/ParserTests/ipi/transformers.expr index bcb40470a87..7fd98212897 100644 --- a/Cabal-tests/tests/ParserTests/ipi/transformers.expr +++ b/Cabal-tests/tests/ParserTests/ipi/transformers.expr @@ -8,6 +8,7 @@ InstalledPackageInfo { sourceLibName = LMainLibName, installedComponentId_ = ComponentId "", + installedSublibs = [], libVisibility = LibraryVisibilityPublic, installedUnitId = UnitId diff --git a/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs b/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs index 93ec21c5b9f..bb3a18e9ec7 100644 --- a/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs +++ b/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs @@ -33,4 +33,4 @@ md5CheckGenericPackageDescription proxy = md5Check proxy md5CheckLocalBuildInfo :: Proxy LocalBuildInfo -> Assertion md5CheckLocalBuildInfo proxy = md5Check proxy - 0x78979713e08179ab070d6ab10cd5ef6c + 0x31137cc4853f0380d1f18eb165d746df diff --git a/Cabal/src/Distribution/Simple/Register.hs b/Cabal/src/Distribution/Simple/Register.hs index 3e603a14d6a..599af063894 100644 --- a/Cabal/src/Distribution/Simple/Register.hs +++ b/Cabal/src/Distribution/Simple/Register.hs @@ -502,6 +502,7 @@ generalInstalledPackageInfo adjustRelIncDirs pkg abi_hash lib lbi clbi installDi { IPI.sourcePackageId = packageId pkg , IPI.installedUnitId = componentUnitId clbi , IPI.installedComponentId_ = componentComponentId clbi + , IPI.installedSublibs = mempty , IPI.instantiatedWith = expectLibraryComponent (maybeComponentInstantiatedWith clbi) , IPI.sourceLibName = libName lib , IPI.compatPackageKey = expectLibraryComponent (maybeComponentCompatPackageKey clbi) diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/ConfiguredConversion.hs b/cabal-install-solver/src/Distribution/Solver/Modular/ConfiguredConversion.hs index 0e2e8ad5baa..e306631aa5a 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/ConfiguredConversion.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/ConfiguredConversion.hs @@ -5,8 +5,11 @@ module Distribution.Solver.Modular.ConfiguredConversion import Data.Maybe import Prelude hiding (pi) import Data.Either (partitionEithers) +import Data.Set (Set) +import qualified Data.Set as S import Distribution.Package (UnitId, packageId) +import Distribution.Types.InstalledPackageInfo (InstalledPackageInfo(installedSublibs)) import qualified Distribution.Simple.PackageIndex as SI @@ -30,9 +33,10 @@ convCP :: SI.InstalledPackageIndex -> CP QPN -> ResolverPackage loc convCP iidx sidx (CP qpi fa es ds) = case convPI qpi of - Left pi -> PreExisting $ + Left (pi, subPis) -> + PreExisting $ InstSolverPackage { - instSolverPkgIPI = fromJust $ SI.lookupUnitId iidx pi, + instSolverPkgIPI = addSublibs iidx subPis $ fromJust $ SI.lookupUnitId iidx pi, instSolverPkgLibDeps = fmap fst ds', instSolverPkgExeDeps = fmap snd ds' } @@ -50,15 +54,29 @@ convCP iidx sidx (CP qpi fa es ds) = ds' :: ComponentDeps ([SolverId] {- lib -}, [SolverId] {- exe -}) ds' = fmap (partitionEithers . map convConfId) ds -convPI :: PI QPN -> Either UnitId PackageId -convPI (PI _ (I _ (Inst pi))) = Left pi -convPI pi = Right (packageId (either id id (convConfId pi))) + addSublibs + :: SI.InstalledPackageIndex + -> Set UnitId + -> InstalledPackageInfo + -> InstalledPackageInfo + addSublibs idx subPis info = + info { installedSublibs = + mapMaybe + (SI.lookupUnitId idx) + (S.toList subPis) + } + +convPI :: PI QPN -> Either (UnitId, Set UnitId) PackageId +convPI (PI _ (I _ (Inst pi))) = Left (pi, mempty) +convPI (PI _ (I _ (InstGroup pi subPis))) = Left (pi, subPis) +convPI pi = Right (packageId (either id id (convConfId pi))) convConfId :: PI QPN -> Either SolverId {- is lib -} SolverId {- is exe -} convConfId (PI (Q (PackagePath _ q) pn) (I v loc)) = case loc of Inst pi -> Left (PreExistingId sourceId pi) - _otherwise + InstGroup pi _subPis -> Left (PreExistingId sourceId pi) + InRepo | QualExe _ pn' <- q -- NB: the dependencies of the executable are also -- qualified. So the way to tell if this is an executable diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs b/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs index f150235631f..6ccdca3290e 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs @@ -6,6 +6,7 @@ import Distribution.Solver.Compat.Prelude import Prelude () import qualified Data.List as L +import qualified Data.Maybe import qualified Data.Map.Strict as M import qualified Distribution.Compat.NonEmptySet as NonEmptySet import qualified Data.Set as S @@ -62,7 +63,80 @@ convPIs :: OS -> Arch -> CompilerInfo -> Map PN [LabeledPackageConstraint] -> Index convPIs os arch comp constraints sip strfl solveExes iidx sidx = mkIndex $ - convIPI' sip iidx ++ convSPI' os arch comp constraints strfl solveExes sidx + groupInstalledSublibs (convIPI' sip iidx) + ++ (convSPI' os arch comp constraints strfl solveExes sidx) + +-- | Group packages with the same package name and version, +-- merge their sub-libraries and dependencies so we get +-- a similar looking package as if it came from repository. +groupInstalledSublibs + :: [(PN, I, PInfo)] + -> [(PN, I, PInfo)] +groupInstalledSublibs xs = + remapPInfoDepsToInstGroups + $ M.elems + $ foldl + (\acc x@(pn, I ver _, _) -> + M.insertWith + (\(_, newI, newInfo) (_, oldI, oldInfo) -> + (pn, mergeIs oldI newI, mergeInfos oldInfo newInfo) + ) + (pn, ver) + x + acc + ) + M.empty + xs + where + -- flags are probably safe to ignore here as they are fixed for installed anyway + mergeInfos :: PInfo -> PInfo -> PInfo + mergeInfos (PInfo deps comps flagNfo fr) (PInfo deps' comps' _flagNfo _fr) = + PInfo + (deps <> deps') + (comps <> comps') + flagNfo + fr + + -- this pass creates InstGroup(s) + mergeIs :: I -> I -> I + mergeIs (I ver (Inst pId)) (I _ver (Inst subPId)) = + I ver (InstGroup pId (S.singleton subPId)) + mergeIs (I ver (InstGroup pId subPIds)) (I _ver (Inst subPId)) = + I ver (InstGroup pId (S.insert subPId subPIds)) + -- XXX/srk, can't really happen as they are lexicographically ordered + mergeIs a b = error $ "Absurd mergeIs" <> show (a,b) + + -- now some deps from convIP/convIPId pass refer to Inst when they should refer to InstGroup + remapPInfoDepsToInstGroups :: [(PN, I, PInfo)] -> [(PN, I, PInfo)] + remapPInfoDepsToInstGroups ps = + let + -- Inst -> InstGroup mapping, other Loc(s) are preserved + locMap :: Map Loc Loc + locMap = + M.fromList + $ concatMap + (\(_pn, I _ver loc, _pInfo) -> case loc of + ip@(Inst _pId) -> + pure (ip, ip) + g@(InstGroup pId subPIds) -> + (Inst pId, g):(map (\x -> (Inst x, g)) (S.toList subPIds)) + InRepo -> + pure (InRepo, InRepo) + ) + ps + + remapDep :: FlaggedDep PN -> FlaggedDep PN + remapDep (D.Simple (LDep dr (Dep depComp (Fixed (I ver loc)))) comp) = + let newLoc = Data.Maybe.fromJust $ M.lookup loc locMap + in (D.Simple (LDep dr (Dep depComp (Fixed (I ver newLoc)))) comp) + remapDep x = x + + in map + (\(pn, i, PInfo deps comps flagNfo fr) -> + (pn, i, PInfo (map remapDep deps) comps flagNfo fr) + ) + ps + -- | Convert a Cabal installed package index to the simpler, -- more uniform index format of the solver. @@ -85,20 +159,26 @@ convIPI' (ShadowPkgs sip) idx = -- | Extract/recover the package ID from an installed package info, and convert it to a solver's I. convId :: IPI.InstalledPackageInfo -> (PN, I) convId ipi = (pn, I ver $ Inst $ IPI.installedUnitId ipi) +-- where (MungedPackageId (MungedPackageName pn _) ver) = mungedId ipi where MungedPackageId mpn ver = mungedId ipi -- HACK. See Note [Index conversion with internal libraries] - pn = encodeCompatPackageName mpn + pn = case IPI.libVisibility ipi of +-- LibraryVisibilityPrivate -> encodeCompatPackageName mpn + LibraryVisibilityPrivate -> pkgName $ IPI.sourcePackageId ipi + LibraryVisibilityPublic -> pkgName $ IPI.sourcePackageId ipi -- | Convert a single installed package into the solver-specific format. -convIP :: SI.InstalledPackageIndex -> IPI.InstalledPackageInfo -> (PN, I, PInfo) +convIP + :: SI.InstalledPackageIndex + -> IPI.InstalledPackageInfo + -> (PN, I, PInfo) convIP idx ipi = case traverse (convIPId (DependencyReason pn M.empty S.empty) comp idx) (IPI.depends ipi) of Left u -> (pn, i, PInfo [] M.empty M.empty (Just (Broken u))) Right fds -> (pn, i, PInfo fds components M.empty Nothing) where - -- TODO: Handle sub-libraries and visibility. components = - M.singleton (ExposedLib LMainLibName) + M.singleton (ExposedLib $ IPI.sourceLibName ipi) ComponentInfo { compIsVisible = IsVisible True , compIsBuildable = IsBuildable True @@ -144,12 +224,16 @@ convIP idx ipi = -- May return Nothing if the package can't be found in the index. That -- indicates that the original package having this dependency is broken -- and should be ignored. -convIPId :: DependencyReason PN -> Component -> SI.InstalledPackageIndex -> UnitId -> Either UnitId (FlaggedDep PN) +convIPId :: DependencyReason PN + -> Component + -> SI.InstalledPackageIndex + -> UnitId + -> Either UnitId (FlaggedDep PN) convIPId dr comp idx ipid = case SI.lookupUnitId idx ipid of Nothing -> Left ipid Just ipi -> let (pn, i) = convId ipi - name = ExposedLib LMainLibName -- TODO: Handle sub-libraries. + name = ExposedLib $ IPI.sourceLibName ipi in Right (D.Simple (LDep dr (Dep (PkgComponent pn name) (Fixed i))) comp) -- NB: something we pick up from the -- InstalledPackageIndex is NEVER an executable diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs index 5dbcce9194c..03c8afb2663 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Message.hs @@ -289,7 +289,7 @@ showOptions q [x] = showOption q x showOptions q xs = showQPN q ++ "; " ++ (L.intercalate ", " [if isJust linkedTo then showOption q x - else showI i -- Don't show the package, just the version + else showI q i -- Don't show the package, just the version | x@(POption i linkedTo) <- xs ]) @@ -351,7 +351,7 @@ showConflictingDep (ConflictingDep dr (PkgComponent qpn comp) ci) = ExposedLib (LSubLibName lib) -> " (lib " ++ unUnqualComponentName lib ++ ")" in case ci of Fixed i -> (if qpn /= qpn' then showDependencyReason dr ++ " => " else "") ++ - showQPN qpn ++ componentStr ++ "==" ++ showI i + showQPN qpn ++ componentStr ++ "==" ++ showI qpn i Constrained vr -> showDependencyReason dr ++ " => " ++ showQPN qpn ++ componentStr ++ showVR vr diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/Package.hs b/cabal-install-solver/src/Distribution/Solver/Modular/Package.hs index 876ac2d790c..9cda3734465 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/Package.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/Package.hs @@ -22,12 +22,16 @@ module Distribution.Solver.Modular.Package import Prelude () import Distribution.Solver.Compat.Prelude +import qualified Data.List as L +import qualified Data.Set as S + import Distribution.Package -- from Cabal import Distribution.Pretty (prettyShow) import Distribution.Solver.Modular.Version import Distribution.Solver.Types.PackagePath + -- | A package name. type PN = PackageName @@ -49,7 +53,10 @@ type PId = UnitId -- package instance via its 'PId'. -- -- TODO: More information is needed about the repo. -data Loc = Inst PId | InRepo +data Loc + = Inst PId + | InstGroup PId (Set PId) + | InRepo deriving (Eq, Ord, Show) -- | Instance. A version number and a location. @@ -57,14 +64,29 @@ data I = I Ver Loc deriving (Eq, Ord, Show) -- | String representation of an instance. -showI :: I -> String -showI (I v InRepo) = showVer v -showI (I v (Inst uid)) = showVer v ++ "/installed" ++ extractPackageAbiHash uid - where - extractPackageAbiHash xs = - case first reverse $ break (=='-') $ reverse (prettyShow xs) of - (ys, []) -> ys - (ys, _) -> '-' : ys +showI :: QPN -> I -> String +showI _qpn (I v InRepo) = showVer v +showI qpn (I v (Inst uid)) = + let + uidPrefix = showQPN qpn <> "-" <> showVer v <> "-" + renderUid u = + case L.stripPrefix uidPrefix (prettyShow u) of + Nothing -> showVer v + Just stripped -> stripped + in + showVer v <> "/installed-" <> renderUid uid +showI qpn (I v (InstGroup uid subUids)) = + let + uidPrefix = showQPN qpn <> "-" <> showVer v <> "-" + renderUid u = + case L.stripPrefix uidPrefix (prettyShow u) of + Nothing -> prettyShow u + Just stripped -> stripped + in + showI qpn (I v (Inst uid)) + <> " installed package group [" + <> unwords (map renderUid $ S.toList subUids) + <> "]" -- | Package instance. A package name and an instance. data PI qpn = PI qpn I @@ -72,7 +94,7 @@ data PI qpn = PI qpn I -- | String representation of a package instance. showPI :: PI QPN -> String -showPI (PI qpn i) = showQPN qpn ++ "-" ++ showI i +showPI (PI qpn i) = showQPN qpn ++ "-" ++ showI qpn i instI :: I -> Bool instI (I _ (Inst _)) = True diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 87b41a0be73..1b473d93d9e 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -1673,18 +1673,19 @@ elaborateInstallPlan preexistingInstantiatedPkgs :: Map UnitId FullUnitId preexistingInstantiatedPkgs = - Map.fromList (mapMaybe f (SolverInstallPlan.toList solverPlan)) + Map.fromList (concat $ mapMaybe f (SolverInstallPlan.toList solverPlan)) where f (SolverInstallPlan.PreExisting inst) | let ipkg = instSolverPkgIPI inst , not (IPI.indefinite ipkg) = - Just - ( IPI.installedUnitId ipkg - , ( FullUnitId - (IPI.installedComponentId ipkg) - (Map.fromList (IPI.instantiatedWith ipkg)) - ) - ) + let mkEntry x = + ( IPI.installedUnitId x + , ( FullUnitId + (IPI.installedComponentId x) + (Map.fromList (IPI.instantiatedWith x)) + ) + ) + in Just $ (mkEntry ipkg) : (map mkEntry (IPI.installedSublibs ipkg)) f _ = Nothing elaboratedInstallPlan @@ -1693,7 +1694,9 @@ elaborateInstallPlan flip InstallPlan.fromSolverInstallPlanWithProgress solverPlan $ \mapDep planpkg -> case planpkg of SolverInstallPlan.PreExisting pkg -> - return [InstallPlan.PreExisting (instSolverPkgIPI pkg)] + return $ + [InstallPlan.PreExisting (instSolverPkgIPI pkg)] + ++ map InstallPlan.PreExisting (IPI.installedSublibs (instSolverPkgIPI pkg)) SolverInstallPlan.Configured pkg -> let inplace_doc | shouldBuildInplaceOnly pkg = text "inplace" @@ -3175,7 +3178,7 @@ availableInstalledTargets ipkg = status = TargetBuildable (unitid, cname) TargetRequestedByDefault target = AvailableTarget (packageId ipkg) cname status False fake = False - in [(packageId ipkg, cname, fake, target)] + in (packageId ipkg, cname, fake, target) : (concatMap availableInstalledTargets (IPI.installedSublibs ipkg)) availableSourceTargets :: ElaboratedConfiguredPackage @@ -3637,10 +3640,11 @@ pruneInstallPlanPass1 pkgs ] availablePkgs = - Set.fromList - [ installedUnitId pkg - | InstallPlan.PreExisting pkg <- pkgs - ] + Set.fromList $ + concat + [ (installedUnitId pkg) : (map installedUnitId (IPI.installedSublibs pkg)) + | InstallPlan.PreExisting pkg <- pkgs + ] {- Note [Pruning for Multi Repl] diff --git a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs index 9ea020bd512..ef1d8da532b 100644 --- a/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs +++ b/cabal-install/tests/UnitTests/Distribution/Solver/Modular/Solver.hs @@ -932,7 +932,7 @@ tests = , runTest $ let db = [ Right $ exAv "my-package" 1 [ExFix "other-package" 3] - , Left $ exInst "other-package" 2 "other-package-AbCdEfGhIj0123456789" [] + , Left $ exInst "other-package" 2 "other-package-2-AbCdEfGhIj0123456789" [] ] msg = "rejecting: other-package-2/installed-AbCdEfGhIj0123456789" in mkTest db "show full installed package ABI hash (issue #5892)" ["my-package"] $ From c936c51dd41df0c42b049649afcd5282dfb79bca Mon Sep 17 00:00:00 2001 From: sorki Date: Sat, 2 May 2026 08:19:17 +0200 Subject: [PATCH 3/9] cabal.project: multi-repl: True --- cabal.project | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cabal.project b/cabal.project index a2075cfdc29..cc64225a0e4 100644 --- a/cabal.project +++ b/cabal.project @@ -12,3 +12,5 @@ package cabal-install package Cabal flags: +git-rev + +multi-repl: True From 072e28fdc1f7c2d096888d244982e03e21fafc8b Mon Sep 17 00:00:00 2001 From: sorki Date: Sat, 2 May 2026 10:21:57 +0200 Subject: [PATCH 4/9] cabal-install: drop ipiComponentName same as `IPI.sourceComponentName` --- cabal-install/src/Distribution/Client/ProjectPlanning.hs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 1b473d93d9e..7fca272a1c4 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -2679,12 +2679,7 @@ shouldBeLocal (SpecificSourcePackage pkg) = case srcpkgSource pkg of -- | Given a 'ElaboratedPlanPackage', report if it matches a 'ComponentName'. matchPlanPkg :: (ComponentName -> Bool) -> ElaboratedPlanPackage -> Bool -matchPlanPkg p = InstallPlan.foldPlanPackage (p . ipiComponentName) (matchElabPkg p) - --- | Get the appropriate 'ComponentName' which identifies an installed --- component. -ipiComponentName :: IPI.InstalledPackageInfo -> ComponentName -ipiComponentName = CLibName . IPI.sourceLibName +matchPlanPkg p = InstallPlan.foldPlanPackage (p . IPI.sourceComponentName) (matchElabPkg p) -- | Given a 'ElaboratedConfiguredPackage', report if it matches a -- 'ComponentName'. @@ -2716,7 +2711,7 @@ mkCCMapping = ( \ipkg -> ( packageName ipkg , Map.singleton - (ipiComponentName ipkg) + (IPI.sourceComponentName ipkg) -- TODO: libify ( AnnotatedId { ann_id = IPI.installedComponentId ipkg From 529e73069bd5d4a75c402b56cefb70d6ab5973b7 Mon Sep 17 00:00:00 2001 From: sorki Date: Sat, 2 May 2026 12:22:26 +0200 Subject: [PATCH 5/9] cabal-install-solver: remove private lib munging from solver This is no longer needed since private sub-libraries won't appear at top level, only as `installedSublibs` of top-level ones. Related to #6039 --- .../Solver/Modular/IndexConversion.hs | 44 +++---------------- .../Solver/Types/InstSolverPackage.hs | 13 +----- 2 files changed, 7 insertions(+), 50 deletions(-) diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs b/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs index 6ccdca3290e..04ec19ba6fc 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs @@ -19,8 +19,6 @@ import Distribution.Types.ExeDependency -- from Cabal import Distribution.Types.PkgconfigDependency -- from Cabal import Distribution.Types.ComponentName -- from Cabal import Distribution.Types.CondTree -- from Cabal -import Distribution.Types.MungedPackageId -- from Cabal -import Distribution.Types.MungedPackageName -- from Cabal import Distribution.PackageDescription -- from Cabal import Distribution.PackageDescription.Configuration import qualified Distribution.Simple.PackageIndex as SI @@ -145,8 +143,7 @@ convIPI' (ShadowPkgs sip) idx = -- apply shadowing whenever there are multiple installed packages with -- the same version [ maybeShadow (convIP idx pkg) - -- IMPORTANT to get internal libraries. See - -- Note [Index conversion with internal libraries] + -- IMPORTANT to use @allPackagesBySourcePackageIdAndLibName@ to get internal libraries | (_, pkgs) <- SI.allPackagesBySourcePackageIdAndLibName idx , (maybeShadow, pkg) <- zip (id : repeat shadow) pkgs ] where @@ -158,14 +155,10 @@ convIPI' (ShadowPkgs sip) idx = -- | Extract/recover the package ID from an installed package info, and convert it to a solver's I. convId :: IPI.InstalledPackageInfo -> (PN, I) -convId ipi = (pn, I ver $ Inst $ IPI.installedUnitId ipi) --- where (MungedPackageId (MungedPackageName pn _) ver) = mungedId ipi - where MungedPackageId mpn ver = mungedId ipi - -- HACK. See Note [Index conversion with internal libraries] - pn = case IPI.libVisibility ipi of --- LibraryVisibilityPrivate -> encodeCompatPackageName mpn - LibraryVisibilityPrivate -> pkgName $ IPI.sourcePackageId ipi - LibraryVisibilityPublic -> pkgName $ IPI.sourcePackageId ipi +convId ipi = ( pkgName spi + , I (pkgVersion spi) $ Inst $ IPI.installedUnitId ipi + ) + where spi = IPI.sourcePackageId ipi -- | Convert a single installed package into the solver-specific format. convIP @@ -191,33 +184,6 @@ convIP idx ipi = comp = componentNameToComponent $ CLibName $ IPI.sourceLibName ipi -- TODO: Installed packages should also store their encapsulations! --- Note [Index conversion with internal libraries] --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- Something very interesting happens when we have internal libraries --- in our index. In this case, we maybe have p-0.1, which itself --- depends on the internal library p-internal ALSO from p-0.1. --- Here's the danger: --- --- - If we treat both of these packages as having PN "p", --- then the solver will try to pick one or the other, --- but never both. --- --- - If we drop the internal packages, now p-0.1 has a --- dangling dependency on an "installed" package we know --- nothing about. Oops. --- --- An expedient hack is to put p-internal into cabal-install's --- index as a MUNGED package name, so that it doesn't conflict --- with anyone else (except other instances of itself). But --- yet, we ought NOT to say that PNs in the solver are munged --- package names, because they're not; for source packages, --- we really will never see munged package names. --- --- The tension here is that the installed package index is actually --- per library, but the solver is per package. We need to smooth --- it over, and munging the package names is a pretty good way to --- do it. - -- | Convert dependencies specified by an installed package id into -- flagged dependencies of the solver. -- diff --git a/cabal-install-solver/src/Distribution/Solver/Types/InstSolverPackage.hs b/cabal-install-solver/src/Distribution/Solver/Types/InstSolverPackage.hs index 871a0dd15a9..719ac0ea2ef 100644 --- a/cabal-install-solver/src/Distribution/Solver/Types/InstSolverPackage.hs +++ b/cabal-install-solver/src/Distribution/Solver/Types/InstSolverPackage.hs @@ -6,12 +6,9 @@ module Distribution.Solver.Types.InstSolverPackage import Distribution.Solver.Compat.Prelude import Prelude () -import Distribution.Package ( Package(..), HasMungedPackageId(..), HasUnitId(..) ) +import Distribution.Package ( Package(..), HasUnitId(..) ) import Distribution.Solver.Types.ComponentDeps ( ComponentDeps ) import Distribution.Solver.Types.SolverId -import Distribution.Types.MungedPackageId -import Distribution.Types.PackageId -import Distribution.Types.MungedPackageName import Distribution.InstalledPackageInfo (InstalledPackageInfo) -- | An 'InstSolverPackage' is a pre-existing installed package @@ -27,13 +24,7 @@ instance Binary InstSolverPackage instance Structured InstSolverPackage instance Package InstSolverPackage where - packageId i = - -- HACK! See Note [Index conversion with internal libraries] - let MungedPackageId mpn v = mungedId i - in PackageIdentifier (encodeCompatPackageName mpn) v - -instance HasMungedPackageId InstSolverPackage where - mungedId = mungedId . instSolverPkgIPI + packageId = packageId . instSolverPkgIPI instance HasUnitId InstSolverPackage where installedUnitId = installedUnitId . instSolverPkgIPI From b4d9c4be42e2fa40ac7ddf98e9242323c22f3d24 Mon Sep 17 00:00:00 2001 From: sorki Date: Wed, 13 May 2026 12:45:20 +0200 Subject: [PATCH 6/9] Introduce InstanceUnitId `InstanceUnitId` serves as a unique identifier for a group of installed packages, allowing us to reconstruct package in solver, similar to how source package looks. Previously solver would treat each component of an installed package as a separate unit, leading to hacks like package name/component munging to prevent conflicts with the main package library. As there was no way to match sub-components to main library from existing `InstalledPackageInfo` fields, we need to add `InstanceUnitId` field, that links sub-components to main library if it exists or to some other component if not, allowing us to easily group components in solvers index conversion pass. --- .../Types/InstalledPackageInfo.hs | 2 + .../InstalledPackageInfo/FieldGrammar.hs | 2 + .../Types/InstalledPackageInfo/Lens.hs | 6 ++- Cabal-syntax/src/Distribution/Types/UnitId.hs | 16 +++++++ .../tests/ParserTests/ipi/Includes2.cabal | 1 + .../tests/ParserTests/ipi/Includes2.expr | 2 + .../tests/ParserTests/ipi/Includes2.format | 1 + .../ipi/internal-preprocessor-test.expr | 2 + .../ParserTests/ipi/issue-2276-ghc-9885.expr | 2 + .../tests/ParserTests/ipi/transformers.cabal | 1 + .../tests/ParserTests/ipi/transformers.expr | 2 + .../tests/ParserTests/ipi/transformers.format | 1 + .../Distribution/Utils/Structured.hs | 2 +- .../src/Data/TreeDiff/Instances/Cabal.hs | 3 +- Cabal/src/Distribution/Backpack/Configure.hs | 5 ++ .../Backpack/ConfiguredComponent.hs | 23 +++++++-- .../Distribution/Backpack/LinkedComponent.hs | 4 ++ .../Distribution/Backpack/ReadyComponent.hs | 4 ++ Cabal/src/Distribution/Simple/Build.hs | 1 + Cabal/src/Distribution/Simple/Configure.hs | 48 ++++++++++++++++++- Cabal/src/Distribution/Simple/Errors.hs | 3 ++ Cabal/src/Distribution/Simple/Register.hs | 1 + Cabal/src/Distribution/Simple/Setup/Config.hs | 2 + .../Types/ComponentLocalBuildInfo.hs | 3 ++ .../src/Distribution/Client/Config.hs | 1 + .../Client/ProjectConfig/Legacy.hs | 2 + .../Distribution/Client/ProjectPlanning.hs | 35 +++++++++++++- .../Client/ProjectPlanning/Types.hs | 3 ++ .../src/Distribution/Client/Setup.hs | 1 + 29 files changed, 170 insertions(+), 9 deletions(-) diff --git a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs index aa5d87666dc..b1758312611 100644 --- a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs +++ b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo.hs @@ -45,6 +45,7 @@ data InstalledPackageInfo = InstalledPackageInfo , installedSublibs :: [InstalledPackageInfo] , libVisibility :: LibraryVisibility , installedUnitId :: UnitId + , installedInstanceUnitId :: InstanceUnitId , -- INVARIANT: if this package is definite, OpenModule's -- OpenUnitId directly records UnitId. If it is -- indefinite, OpenModule is always an OpenModuleVar @@ -136,6 +137,7 @@ emptyInstalledPackageInfo = , sourceLibName = LMainLibName , installedComponentId_ = mkComponentId "" , installedUnitId = mkUnitId "" + , installedInstanceUnitId = mkInstanceUnitId $ mkUnitId "" , installedSublibs = mempty , instantiatedWith = [] , compatPackageKey = "" diff --git a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs index 56f2381559b..78283c238fc 100644 --- a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs +++ b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs @@ -61,6 +61,7 @@ ipiFieldGrammar , c (Identity LibraryVisibility) , c (Identity PackageName) , c (Identity UnitId) + , c (Identity InstanceUnitId) , c (Identity UnqualComponentName) , c (List FSep (Identity AbiDependency) AbiDependency) , c (List FSep (Identity UnitId) UnitId) @@ -85,6 +86,7 @@ ipiFieldGrammar = <@> blurFieldGrammar basic basicFieldGrammar -- Basic fields <@> optionalFieldDef "id" L.installedUnitId (mkUnitId "") + <@> optionalFieldDef "instance-id" L.installedInstanceUnitId (mkInstanceUnitId $ mkUnitId "") <@> optionalFieldDefAla "instantiated-with" InstWith L.instantiatedWith [] <@> optionalFieldDefAla "key" CompatPackageKey L.compatPackageKey "" <@> optionalFieldDefAla "license" SpecLicenseLenient L.license (Left SPDX.NONE) diff --git a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/Lens.hs b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/Lens.hs index 5ced273f460..cbd53817bee 100644 --- a/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/Lens.hs +++ b/Cabal-syntax/src/Distribution/Types/InstalledPackageInfo/Lens.hs @@ -10,7 +10,7 @@ import Prelude () import Distribution.Backpack (OpenModule) import Distribution.License (License) import Distribution.ModuleName (ModuleName) -import Distribution.Package (AbiHash, ComponentId, PackageIdentifier, UnitId) +import Distribution.Package (AbiHash, ComponentId, InstanceUnitId, PackageIdentifier, UnitId) import Distribution.Types.InstalledPackageInfo (AbiDependency, ExposedModule, InstalledPackageInfo) import Distribution.Types.LibraryName (LibraryName) import Distribution.Types.LibraryVisibility (LibraryVisibility) @@ -27,6 +27,10 @@ installedUnitId :: Lens' InstalledPackageInfo UnitId installedUnitId f s = fmap (\x -> s{T.installedUnitId = x}) (f (T.installedUnitId s)) {-# INLINE installedUnitId #-} +installedInstanceUnitId :: Lens' InstalledPackageInfo InstanceUnitId +installedInstanceUnitId f s = fmap (\x -> s{T.installedInstanceUnitId = x}) (f (T.installedInstanceUnitId s)) +{-# INLINE installedInstanceUnitId #-} + installedComponentId_ :: Lens' InstalledPackageInfo ComponentId installedComponentId_ f s = fmap (\x -> s{T.installedComponentId_ = x}) (f (T.installedComponentId_ s)) {-# INLINE installedComponentId_ #-} diff --git a/Cabal-syntax/src/Distribution/Types/UnitId.hs b/Cabal-syntax/src/Distribution/Types/UnitId.hs index 0b5ca4bdf7b..7f5538493c1 100644 --- a/Cabal-syntax/src/Distribution/Types/UnitId.hs +++ b/Cabal-syntax/src/Distribution/Types/UnitId.hs @@ -13,6 +13,9 @@ module Distribution.Types.UnitId , newSimpleUnitId , mkLegacyUnitId , getHSLibraryName + , InstanceUnitId + , mkInstanceUnitId + , unInstanceUnitId ) where import Distribution.Compat.Prelude @@ -131,3 +134,16 @@ instance Parsec DefUnitId where -- is to ensure that the 'DefUnitId' invariant holds. unsafeMkDefUnitId :: UnitId -> DefUnitId unsafeMkDefUnitId = DefUnitId + +-- | A 'UnitId' for an instance (group of components). Typically +-- this matches a main library 'UnitId' +newtype InstanceUnitId = InstanceUnitId {unInstanceUnitId :: UnitId} + deriving (Generic, Read, Show, Eq, Ord, Data, Binary, NFData, Pretty) + +instance Structured InstanceUnitId + +instance Parsec InstanceUnitId where + parsec = InstanceUnitId <$> parsec + +mkInstanceUnitId :: UnitId -> InstanceUnitId +mkInstanceUnitId = InstanceUnitId diff --git a/Cabal-tests/tests/ParserTests/ipi/Includes2.cabal b/Cabal-tests/tests/ParserTests/ipi/Includes2.cabal index efaad1e6540..6c6ccbaca0c 100644 --- a/Cabal-tests/tests/ParserTests/ipi/Includes2.cabal +++ b/Cabal-tests/tests/ParserTests/ipi/Includes2.cabal @@ -1,6 +1,7 @@ name: z-Includes2-z-mylib version: 0.1.0.0 id: Includes2-0.1.0.0-inplace-mylib+3gY9SyjX86dBypHcOaev1n +instance-id: Includes2-0.1.0.0-inplace-mylib+3gY9SyjX86dBypHcOaev1n instantiated-with: Database=Includes2-0.1.0.0-inplace-mysql:Database.MySQL package-name: Includes2 lib-name: mylib diff --git a/Cabal-tests/tests/ParserTests/ipi/Includes2.expr b/Cabal-tests/tests/ParserTests/ipi/Includes2.expr index 4459fffc7f8..f0ed3e0d738 100644 --- a/Cabal-tests/tests/ParserTests/ipi/Includes2.expr +++ b/Cabal-tests/tests/ParserTests/ipi/Includes2.expr @@ -14,6 +14,8 @@ InstalledPackageInfo { LibraryVisibilityPrivate, installedUnitId = UnitId "Includes2-0.1.0.0-inplace-mylib+3gY9SyjX86dBypHcOaev1n", + installedInstanceUnitId = InstanceUnitId + (UnitId "Includes2-0.1.0.0-inplace-mylib+3gY9SyjX86dBypHcOaev1n"), instantiatedWith = [ _×_ (ModuleName "Database") diff --git a/Cabal-tests/tests/ParserTests/ipi/Includes2.format b/Cabal-tests/tests/ParserTests/ipi/Includes2.format index ab6445d811d..3eece63182c 100644 --- a/Cabal-tests/tests/ParserTests/ipi/Includes2.format +++ b/Cabal-tests/tests/ParserTests/ipi/Includes2.format @@ -3,6 +3,7 @@ version: 0.1.0.0 package-name: Includes2 lib-name: mylib id: Includes2-0.1.0.0-inplace-mylib+3gY9SyjX86dBypHcOaev1n +instance-id: Includes2-0.1.0.0-inplace-mylib+3gY9SyjX86dBypHcOaev1n instantiated-with: Database=Includes2-0.1.0.0-inplace-mysql:Database.MySQL key: Includes2-0.1.0.0-inplace-mylib+3gY9SyjX86dBypHcOaev1n license: BSD3 diff --git a/Cabal-tests/tests/ParserTests/ipi/internal-preprocessor-test.expr b/Cabal-tests/tests/ParserTests/ipi/internal-preprocessor-test.expr index a0a9e419216..350a782ee81 100644 --- a/Cabal-tests/tests/ParserTests/ipi/internal-preprocessor-test.expr +++ b/Cabal-tests/tests/ParserTests/ipi/internal-preprocessor-test.expr @@ -13,6 +13,8 @@ InstalledPackageInfo { LibraryVisibilityPublic, installedUnitId = UnitId "internal-preprocessor-test-0.1.0.0", + installedInstanceUnitId = InstanceUnitId + (UnitId ""), instantiatedWith = [], compatPackageKey = "internal-preprocessor-test-0.1.0.0", diff --git a/Cabal-tests/tests/ParserTests/ipi/issue-2276-ghc-9885.expr b/Cabal-tests/tests/ParserTests/ipi/issue-2276-ghc-9885.expr index a652d1dcf09..9e57e7896ca 100644 --- a/Cabal-tests/tests/ParserTests/ipi/issue-2276-ghc-9885.expr +++ b/Cabal-tests/tests/ParserTests/ipi/issue-2276-ghc-9885.expr @@ -13,6 +13,8 @@ InstalledPackageInfo { LibraryVisibilityPublic, installedUnitId = UnitId "transformers-0.5.2.0", + installedInstanceUnitId = InstanceUnitId + (UnitId ""), instantiatedWith = [], compatPackageKey = "transformers-0.5.2.0", diff --git a/Cabal-tests/tests/ParserTests/ipi/transformers.cabal b/Cabal-tests/tests/ParserTests/ipi/transformers.cabal index 2c6387c2206..5329fd959ea 100644 --- a/Cabal-tests/tests/ParserTests/ipi/transformers.cabal +++ b/Cabal-tests/tests/ParserTests/ipi/transformers.cabal @@ -1,6 +1,7 @@ name: transformers version: 0.5.2.0 id: transformers-0.5.2.0 +instance-id: transformers-0.5.2.0 key: transformers-0.5.2.0 license: BSD3 maintainer: Ross Paterson diff --git a/Cabal-tests/tests/ParserTests/ipi/transformers.expr b/Cabal-tests/tests/ParserTests/ipi/transformers.expr index 7fd98212897..cd2a369d90e 100644 --- a/Cabal-tests/tests/ParserTests/ipi/transformers.expr +++ b/Cabal-tests/tests/ParserTests/ipi/transformers.expr @@ -13,6 +13,8 @@ InstalledPackageInfo { LibraryVisibilityPublic, installedUnitId = UnitId "transformers-0.5.2.0", + installedInstanceUnitId = InstanceUnitId + (UnitId "transformers-0.5.2.0"), instantiatedWith = [], compatPackageKey = "transformers-0.5.2.0", diff --git a/Cabal-tests/tests/ParserTests/ipi/transformers.format b/Cabal-tests/tests/ParserTests/ipi/transformers.format index 66a0e14e5b5..7de14b06258 100644 --- a/Cabal-tests/tests/ParserTests/ipi/transformers.format +++ b/Cabal-tests/tests/ParserTests/ipi/transformers.format @@ -2,6 +2,7 @@ name: transformers version: 0.5.2.0 visibility: public id: transformers-0.5.2.0 +instance-id: transformers-0.5.2.0 key: transformers-0.5.2.0 license: BSD3 maintainer: Ross Paterson diff --git a/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs b/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs index bb3a18e9ec7..734efdd45d6 100644 --- a/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs +++ b/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs @@ -33,4 +33,4 @@ md5CheckGenericPackageDescription proxy = md5Check proxy md5CheckLocalBuildInfo :: Proxy LocalBuildInfo -> Assertion md5CheckLocalBuildInfo proxy = md5Check proxy - 0x31137cc4853f0380d1f18eb165d746df + 0xe394537933f4b964768db669ff128aa7 diff --git a/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs b/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs index f8eaad4bf88..f6e5fc093e1 100644 --- a/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs +++ b/Cabal-tree-diff/src/Data/TreeDiff/Instances/Cabal.hs @@ -27,7 +27,7 @@ import Distribution.Types.AbiHash (AbiHash) import Distribution.Types.ComponentId (ComponentId) import Distribution.Types.DumpBuildInfo (DumpBuildInfo) import Distribution.Types.PackageVersionConstraint -import Distribution.Types.UnitId (DefUnitId, UnitId) +import Distribution.Types.UnitId (DefUnitId, InstanceUnitId, UnitId) import Distribution.Utils.NubList (NubList) import Distribution.Utils.Path (SymbolicPathX) import Distribution.Utils.ShortText (ShortText, fromShortText) @@ -113,6 +113,7 @@ instance ToExpr ForeignLibOption instance ToExpr ForeignLibType instance ToExpr HaddockTarget instance ToExpr IncludeRenaming +instance ToExpr InstanceUnitId instance ToExpr InstalledPackageInfo instance ToExpr KnownRepoType instance ToExpr LegacyExeDependency diff --git a/Cabal/src/Distribution/Backpack/Configure.hs b/Cabal/src/Distribution/Backpack/Configure.hs index 55d1ae03254..fb6f91e69af 100644 --- a/Cabal/src/Distribution/Backpack/Configure.hs +++ b/Cabal/src/Distribution/Backpack/Configure.hs @@ -67,6 +67,7 @@ configureComponentLocalBuildInfos -> Bool -- deterministic -> Flag String -- configIPID -> Flag ComponentId -- configCID + -> Flag InstanceUnitId -- configIUID -> PackageDescription -> ([PreExistingComponent], [ConfiguredPromisedComponent]) -> FlagAssignment -- configConfigurationsFlags @@ -81,6 +82,7 @@ configureComponentLocalBuildInfos deterministic ipid_flag cid_flag + iuid_flag pkg_descr (prePkgDeps, promisedPkgDeps) flags @@ -127,6 +129,7 @@ configureComponentLocalBuildInfos deterministic ipid_flag cid_flag + iuid_flag pkg_descr conf_pkg_map (map fst graph0) @@ -391,6 +394,7 @@ mkLinkedComponentsLocalBuildInfo comp rcs = map go rcs in LibComponentLocalBuildInfo { componentPackageDeps = cpds , componentUnitId = this_uid + , componentInstanceUnitId = this_instance_uid , componentComponentId = this_cid , componentInstantiatedWith = insts , componentIsIndefinite_ = is_indefinite @@ -445,6 +449,7 @@ mkLinkedComponentsLocalBuildInfo comp rcs = map go rcs } where this_uid = rc_uid rc + this_instance_uid = rc_instance_id rc this_open_uid = rc_open_uid rc this_cid = rc_cid rc cname = componentName (rc_component rc) diff --git a/Cabal/src/Distribution/Backpack/ConfiguredComponent.hs b/Cabal/src/Distribution/Backpack/ConfiguredComponent.hs index 9fd78352b7c..569de9cf988 100644 --- a/Cabal/src/Distribution/Backpack/ConfiguredComponent.hs +++ b/Cabal/src/Distribution/Backpack/ConfiguredComponent.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE PatternSynonyms #-} + -- | See module Distribution.Backpack.ConfiguredComponent ( ConfiguredComponent (..) @@ -22,7 +24,7 @@ import Distribution.CabalSpecVersion import Distribution.Package import Distribution.PackageDescription import Distribution.Simple.BuildToolDepends -import Distribution.Simple.Flag (Flag) +import Distribution.Simple.Flag (Flag, pattern Flag, pattern NoFlag) import Distribution.Simple.LocalBuildInfo import Distribution.Types.AnnotatedId import Distribution.Types.ComponentInclude @@ -43,6 +45,7 @@ import qualified Text.PrettyPrint as PP data ConfiguredComponent = ConfiguredComponent { cc_ann_id :: AnnotatedId ComponentId -- ^ Unique identifier of component, plus extra useful info. + , cc_instance_unit_id :: InstanceUnitId , cc_component :: Component -- ^ The fragment of syntax from the Cabal file describing this -- component. @@ -98,11 +101,12 @@ dispConfiguredComponent cc = mkConfiguredComponent :: PackageDescription -> ComponentId + -> InstanceUnitId -> [AnnotatedId ComponentId] -- lib deps -> [AnnotatedId ComponentId] -- exe deps -> Component -> LogProgress ConfiguredComponent -mkConfiguredComponent pkg_descr this_cid lib_deps exe_deps component = do +mkConfiguredComponent pkg_descr this_cid this_iuid lib_deps exe_deps component = do -- Resolve each @mixins@ into the actual dependency -- from @lib_deps@. explicit_includes <- forM (mixins bi) $ \(Mixin pn ln rns) -> do @@ -142,6 +146,7 @@ mkConfiguredComponent pkg_descr this_cid lib_deps exe_deps component = do , ann_pid = package pkg_descr , ann_cname = componentName component } + , cc_instance_unit_id = this_iuid , cc_component = component , cc_public = is_public , cc_exe_deps = exe_deps @@ -170,11 +175,12 @@ type ConfiguredComponentMap = toConfiguredComponent :: PackageDescription -> ComponentId + -> InstanceUnitId -> ConfiguredComponentMap -> ConfiguredComponentMap -> Component -> LogProgress ConfiguredComponent -toConfiguredComponent pkg_descr this_cid lib_dep_map exe_dep_map component = do +toConfiguredComponent pkg_descr this_cid this_iuid lib_dep_map exe_dep_map component = do lib_deps <- if newPackageDepsBehaviour pkg_descr then fmap concat $ @@ -202,6 +208,7 @@ toConfiguredComponent pkg_descr this_cid lib_dep_map exe_dep_map component = do mkConfiguredComponent pkg_descr this_cid + this_iuid lib_deps exe_deps component @@ -246,6 +253,7 @@ toConfiguredComponent' -> Bool -- deterministic -> Flag String -- configIPID (todo: remove me) -> Flag ComponentId -- configCID + -> Flag InstanceUnitId -- configIUID -> ConfiguredComponentMap -> Component -> LogProgress ConfiguredComponent @@ -256,12 +264,14 @@ toConfiguredComponent' deterministic ipid_flag cid_flag + iuid_flag dep_map component = do cc <- toConfiguredComponent pkg_descr this_cid + this_iuid dep_map dep_map component @@ -279,6 +289,10 @@ toConfiguredComponent' (package pkg_descr) (componentName component) (Just (deps, flags)) + this_iuid = + case iuid_flag of + Flag iuid -> iuid + NoFlag -> mkInstanceUnitId $ mkUnitId $ unComponentId this_cid deps = [ ann_id aid | m <- Map.elems dep_map, aid <- Map.elems m ] @@ -308,6 +322,7 @@ toConfiguredComponents -> Bool -- deterministic -> Flag String -- configIPID -> Flag ComponentId -- configCID + -> Flag InstanceUnitId -- configIUID -> PackageDescription -> ConfiguredComponentMap -> [Component] @@ -318,6 +333,7 @@ toConfiguredComponents deterministic ipid_flag cid_flag + iuid_flag pkg_descr dep_map comps = @@ -332,6 +348,7 @@ toConfiguredComponents deterministic ipid_flag cid_flag + iuid_flag m component return (extendConfiguredComponentMap cc m, cc) diff --git a/Cabal/src/Distribution/Backpack/LinkedComponent.hs b/Cabal/src/Distribution/Backpack/LinkedComponent.hs index d041c715aac..961a604ec74 100644 --- a/Cabal/src/Distribution/Backpack/LinkedComponent.hs +++ b/Cabal/src/Distribution/Backpack/LinkedComponent.hs @@ -50,6 +50,8 @@ import Text.PrettyPrint (Doc, hang, hsep, quotes, text, vcat, ($+$)) data LinkedComponent = LinkedComponent { lc_ann_id :: AnnotatedId ComponentId -- ^ Uniquely identifies linked component + , lc_instance_id :: InstanceUnitId + -- ^ Uniquely identifies instance (group of components) , lc_component :: Component -- ^ Corresponds to 'cc_component'. , lc_exe_deps :: [AnnotatedId OpenUnitId] @@ -128,6 +130,7 @@ toLinkedComponent pkg_map ConfiguredComponent { cc_ann_id = aid@AnnotatedId{ann_id = this_cid} + , cc_instance_unit_id = iuid , cc_component = component , cc_exe_deps = exe_deps , cc_public = is_public @@ -378,6 +381,7 @@ toLinkedComponent return $ LinkedComponent { lc_ann_id = aid + , lc_instance_id = iuid , lc_component = component , lc_public = is_public , -- These must be executables diff --git a/Cabal/src/Distribution/Backpack/ReadyComponent.hs b/Cabal/src/Distribution/Backpack/ReadyComponent.hs index 3eef45fadbb..143a49435a5 100644 --- a/Cabal/src/Distribution/Backpack/ReadyComponent.hs +++ b/Cabal/src/Distribution/Backpack/ReadyComponent.hs @@ -55,6 +55,8 @@ import Distribution.Version -- for every way these packages can be fully instantiated. data ReadyComponent = ReadyComponent { rc_ann_id :: AnnotatedId UnitId + , rc_instance_id :: InstanceUnitId + -- ^ Uniquely identifies instance (group of components) , rc_open_uid :: OpenUnitId -- ^ The 'OpenUnitId' for this package. At the moment, this -- is used in only one case, which is to determine if an @@ -319,6 +321,7 @@ toReadyComponents pid_map subst0 comps = Just ReadyComponent { rc_ann_id = (lc_ann_id lc){ann_id = uid} + , rc_instance_id = lc_instance_id lc , rc_open_uid = DefiniteUnitId (unsafeMkDefUnitId uid) , rc_cid = lc_cid lc , rc_component = lc_component lc @@ -387,6 +390,7 @@ toReadyComponents pid_map subst0 comps = Just ReadyComponent { rc_ann_id = (lc_ann_id lc){ann_id = uid} + , rc_instance_id = lc_instance_id lc , rc_cid = lc_cid lc , rc_open_uid = lc_uid lc , rc_component = lc_component lc diff --git a/Cabal/src/Distribution/Simple/Build.hs b/Cabal/src/Distribution/Simple/Build.hs index e79a1924eb9..0a661690b88 100644 --- a/Cabal/src/Distribution/Simple/Build.hs +++ b/Cabal/src/Distribution/Simple/Build.hs @@ -851,6 +851,7 @@ testSuiteLibV09AsLibAndExe , componentIsPublic = False , componentIncludes = componentIncludes clbi , componentUnitId = componentUnitId clbi + , componentInstanceUnitId = mkInstanceUnitId $ componentUnitId clbi , componentComponentId = componentComponentId clbi , componentInstantiatedWith = [] , componentCompatPackageName = compat_name diff --git a/Cabal/src/Distribution/Simple/Configure.hs b/Cabal/src/Distribution/Simple/Configure.hs index df1acb72658..01214d6e7d3 100644 --- a/Cabal/src/Distribution/Simple/Configure.hs +++ b/Cabal/src/Distribution/Simple/Configure.hs @@ -721,6 +721,10 @@ preConfigurePackage verbHandles cfg g_pkg_descr = do when (isJust (flagToMaybe (configCID cfg)) && isNothing mb_cname) $ dieWithException verbosity ConfigCIDValidForPreComponent + -- configIUID is only valid for per-component configure + when (isJust (flagToMaybe (configIUID cfg)) && isNothing mb_cname) $ + dieWithException verbosity ConfigIUIDValidForPreComponent + -- Make a data structure describing what components are enabled. let enabled :: ComponentRequestedSpec enabled = @@ -1427,7 +1431,7 @@ configureComponents -- components (which may build-depends on each other) and form a graph. -- From there, we build a ComponentLocalBuildInfo for each of the -- components, which lets us actually build each component. - ( buildComponents :: [ComponentLocalBuildInfo] + ( buildComponents0 :: [ComponentLocalBuildInfo] , packageDependsIndex :: InstalledPackageIndex ) <- runLogProgress verbosity $ @@ -1438,6 +1442,7 @@ configureComponents (fromFlagOrDefault False (configDeterministic cfg)) (configIPID cfg) (configCID cfg) + (configIUID cfg) pkg_descr externalPkgDeps (configConfigurationsFlags cfg) @@ -1445,6 +1450,47 @@ configureComponents installedPackageSet comp + -- Set a common 'InstanceUnitId' for all components so they can be + -- identified as belonging to the same group. + -- + -- If the package has a main library, its 'InstanceUnitId' is used. + -- Otherwise, an arbitrary component's 'InstanceUnitId' is used. + -- + -- Only for 'ComponentRequestedSpec'. + -- For 'OneComponentRequestedSpec', 'InstanceUnitId' is passed via 'configIUID' flag. + let maybeSetCommonInstanceUnitId = + case enabled of + -- Whole package configure, set InstanceUnitIDs + ComponentRequestedSpec{} -> + let + setCommonInstanceUnitId + :: [ComponentLocalBuildInfo] + -> [ComponentLocalBuildInfo] + setCommonInstanceUnitId [] = [] + setCommonInstanceUnitId [single] = [single] + setCommonInstanceUnitId comps@(fstComp : _rest) = + let + assignOne newId LibComponentLocalBuildInfo{..} = LibComponentLocalBuildInfo{componentInstanceUnitId = newId, ..} + assignOne _newId other = other + + assignAll newId = map (assignOne newId) comps + + matchMainLib lclbi@(LibComponentLocalBuildInfo{}) = componentLocalName lclbi == CLibName LMainLibName + matchMainLib _ = False + in + case find matchMainLib comps of + Nothing -> + -- no main library, use arbitrary (first one) for componentInstanceUnitId assignment + assignAll (componentInstanceUnitId fstComp) + Just mainLib -> + assignAll (componentInstanceUnitId mainLib) + in + setCommonInstanceUnitId + -- Component configure, InstanceUnitID passed via flag + OneComponentRequestedSpec{} -> id + + let buildComponents = maybeSetCommonInstanceUnitId buildComponents0 + let buildComponentsMap = foldl' ( \m clbi -> diff --git a/Cabal/src/Distribution/Simple/Errors.hs b/Cabal/src/Distribution/Simple/Errors.hs index 4dc21def00d..6830af76aa7 100644 --- a/Cabal/src/Distribution/Simple/Errors.hs +++ b/Cabal/src/Distribution/Simple/Errors.hs @@ -115,6 +115,7 @@ data CabalException | NoValidComponent | ConfigureEitherSingleOrAll | ConfigCIDValidForPreComponent + | ConfigIUIDValidForPreComponent | SanityCheckForEnableComponents | SanityCheckForDynamicStaticLinking | UnsupportedLanguages PackageIdentifier CompilerId [String] @@ -249,6 +250,7 @@ exceptionCode e = case e of NoValidComponent{} -> 5680 ConfigureEitherSingleOrAll{} -> 2001 ConfigCIDValidForPreComponent{} -> 7006 + ConfigIUIDValidForPreComponent{} -> 7007 SanityCheckForEnableComponents{} -> 5004 SanityCheckForDynamicStaticLinking{} -> 4007 UnsupportedLanguages{} -> 8074 @@ -510,6 +512,7 @@ exceptionMessage e = case e of NoValidComponent -> "No valid component targets found" ConfigureEitherSingleOrAll -> "Can only configure either a single component or all of them" ConfigCIDValidForPreComponent -> "--cid is only supported for per-component configure" + ConfigIUIDValidForPreComponent -> "--iuid is only supported for per-component configure" SanityCheckForEnableComponents -> "--enable-tests/--enable-benchmarks are incompatible with" ++ " explicitly specifying a component to configure." diff --git a/Cabal/src/Distribution/Simple/Register.hs b/Cabal/src/Distribution/Simple/Register.hs index 599af063894..22ba700cf15 100644 --- a/Cabal/src/Distribution/Simple/Register.hs +++ b/Cabal/src/Distribution/Simple/Register.hs @@ -501,6 +501,7 @@ generalInstalledPackageInfo adjustRelIncDirs pkg abi_hash lib lbi clbi installDi IPI.InstalledPackageInfo { IPI.sourcePackageId = packageId pkg , IPI.installedUnitId = componentUnitId clbi + , IPI.installedInstanceUnitId = componentInstanceUnitId clbi , IPI.installedComponentId_ = componentComponentId clbi , IPI.installedSublibs = mempty , IPI.instantiatedWith = expectLibraryComponent (maybeComponentInstantiatedWith clbi) diff --git a/Cabal/src/Distribution/Simple/Setup/Config.hs b/Cabal/src/Distribution/Simple/Setup/Config.hs index 92fb879df9a..4652ac15c0b 100644 --- a/Cabal/src/Distribution/Simple/Setup/Config.hs +++ b/Cabal/src/Distribution/Simple/Setup/Config.hs @@ -164,6 +164,8 @@ data ConfigFlags = ConfigFlags -- ^ explicit IPID to be used , configCID :: Flag ComponentId -- ^ explicit CID to be used + , configIUID :: Flag InstanceUnitId + -- ^ explicit InstanceUnitID to be used , configDeterministic :: Flag Bool -- ^ be as deterministic as possible -- (e.g., invariant over GHC, database, diff --git a/Cabal/src/Distribution/Types/ComponentLocalBuildInfo.hs b/Cabal/src/Distribution/Types/ComponentLocalBuildInfo.hs index 32f4aab473b..354b2354375 100644 --- a/Cabal/src/Distribution/Types/ComponentLocalBuildInfo.hs +++ b/Cabal/src/Distribution/Types/ComponentLocalBuildInfo.hs @@ -39,6 +39,9 @@ data ComponentLocalBuildInfo , componentUnitId :: UnitId -- ^ The computed 'UnitId' which uniquely identifies this -- component. Might be hashed. + , componentInstanceUnitId :: InstanceUnitId + -- ^ The 'InstanceUnitId' which uniquely identifies package instance + -- (equal to componentUnitId for main component, sub components inherit it from main) , componentIsIndefinite_ :: Bool -- ^ Is this an indefinite component (i.e. has unfilled holes)? , componentInstantiatedWith :: [(ModuleName, OpenModule)] diff --git a/cabal-install/src/Distribution/Client/Config.hs b/cabal-install/src/Distribution/Client/Config.hs index dd353e422a6..b050ef7a5a8 100644 --- a/cabal-install/src/Distribution/Client/Config.hs +++ b/cabal-install/src/Distribution/Client/Config.hs @@ -527,6 +527,7 @@ instance Semigroup SavedConfig where , configDeterministic = combine configDeterministic , configIPID = combine configIPID , configCID = combine configCID + , configIUID = combine configIUID , configUserInstall = combine configUserInstall , -- TODO: NubListify configPackageDBs = lastNonEmpty configPackageDBs diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 2168e9054c7..d29b5f24db2 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -1105,6 +1105,7 @@ convertToLegacyAllPackageConfig , configDeterministic = mempty , configIPID = mempty , configCID = mempty + , configIUID = mempty , configConfigurationsFlags = mempty , configTests = mempty , configCoverage = mempty -- TODO: don't merge @@ -1182,6 +1183,7 @@ convertToLegacyPerPackageConfig PackageConfig{..} = , configExtraIncludeDirs = fmap makeSymbolicPath packageConfigExtraIncludeDirs , configIPID = mempty , configCID = mempty + , configIUID = mempty , configDeterministic = mempty , configConfigurationsFlags = packageConfigFlagAssignment , configTests = packageConfigTests diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 7fca272a1c4..d2f7e86f694 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -1707,7 +1707,28 @@ elaborateInstallPlan <+> text "package" <+> quotes (pretty (packageId pkg)) ) - $ map InstallPlan.Configured <$> elaborateSolverToComponents mapDep pkg + $ map InstallPlan.Configured . setCommonInstanceUnitId <$> elaborateSolverToComponents mapDep pkg + + -- Set a common 'InstanceUnitId' for all components so they can be + -- identified as belonging to the same group. + -- + -- If the package has a main library, its 'InstanceUnitId' is used. + -- Otherwise, an arbitrary component's 'InstanceUnitId' is used. + setCommonInstanceUnitId + :: [ElaboratedConfiguredPackage] + -> [ElaboratedConfiguredPackage] + setCommonInstanceUnitId [] = [] + setCommonInstanceUnitId [single] = [single] + setCommonInstanceUnitId elabPkgs@(elabPkg : _rest) = + let + assignAll instanceUnitId = map (\x -> x{elabInstanceUnitId = instanceUnitId}) elabPkgs + in + case find (matchElabPkg (== (CLibName LMainLibName))) elabPkgs of + Nothing -> + -- no main library, use arbitrary (first one) for elabInstanceUnitID assignment + assignAll (elabInstanceUnitId elabPkg) + Just mainLib -> + assignAll (elabInstanceUnitId mainLib) -- NB: We don't INSTANTIATE packages at this point. That's -- a post-pass. This makes it simpler to compute dependencies. @@ -1822,6 +1843,7 @@ elaborateInstallPlan elab0 { elabModuleShape = emptyModuleShape , elabUnitId = notImpl "elabUnitId" + , elabInstanceUnitId = notImpl "elabInstanceUnitId" , elabComponentId = notImpl "elabComponentId" , elabLinkedInstantiatedWith = Map.empty , elabInstallDirs = notImpl "elabInstallDirs" @@ -1881,6 +1903,7 @@ elaborateInstallPlan toConfiguredComponent pd (error "Distribution.Client.ProjectPlanning.cc_cid: filled in later") + (error "Distribution.Client.ProjectPlanning.cc_instance_unit_id: filled in later") (Map.unionWith Map.union external_lib_cc_map cc_map) (Map.unionWith Map.union external_exe_cc_map cc_map) comp @@ -1930,7 +1953,11 @@ elaborateInstallPlan elaboratedSharedConfig elab1 -- knot tied ) - cc = cc0{cc_ann_id = fmap (const cid) (cc_ann_id cc0)} + cc = + cc0 + { cc_ann_id = fmap (const cid) (cc_ann_id cc0) + , cc_instance_unit_id = mkInstanceUnitId $ mkUnitId $ unComponentId cid + } infoProgress $ dispConfiguredComponent cc -- 4. Perform mix-in linking @@ -1958,6 +1985,7 @@ elaborateInstallPlan elab1 { elabModuleShape = lc_shape lc , elabUnitId = abstractUnitId (lc_uid lc) + , elabInstanceUnitId = lc_instance_id lc , elabComponentId = lc_cid lc , elabLinkedInstantiatedWith = Map.fromList (lc_insts lc) , elabPkgOrComp = @@ -2126,6 +2154,7 @@ elaborateInstallPlan elab0 { elabUnitId = newSimpleUnitId pkgInstalledId , elabComponentId = pkgInstalledId + , elabInstanceUnitId = mkInstanceUnitId $ newSimpleUnitId pkgInstalledId , elabLinkedInstantiatedWith = Map.empty , elabPkgOrComp = ElabPackage $ ElaboratedPackage{..} , elabModuleShape = modShape @@ -2230,6 +2259,7 @@ elaborateInstallPlan -- These get filled in later elabUnitId = error "elaborateSolverToCommon: elabUnitId" + elabInstanceUnitId = error "elaborateSolverToCommon: elabInstanceUnitId" elabComponentId = error "elaborateSolverToCommon: elabComponentId" elabInstantiatedWith = Map.empty elabLinkedInstantiatedWith = error "elaborateSolverToCommon: elabLinkedInstantiatedWith" @@ -4121,6 +4151,7 @@ setupHsConfigureFlags configCID = case elabPkgOrComp of ElabPackage _ -> mempty ElabComponent _ -> toFlag elabComponentId + configIUID = toFlag elabInstanceUnitId configProgramPaths = Map.toList elabProgramPaths configProgramArgs = Map.toList elabProgramArgs diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs b/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs index adbd8a85f5e..ed56682b388 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning/Types.hs @@ -203,6 +203,9 @@ instance Structured ElaboratedSharedConfig data ElaboratedConfiguredPackage = ElaboratedConfiguredPackage { elabUnitId :: UnitId -- ^ The 'UnitId' which uniquely identifies this item in a build plan + , elabInstanceUnitId :: InstanceUnitId + -- ^ The 'InstanceUnitId' which uniquely identifies package instance + -- (equal to elabUnitId for main component, sub components inherit it from main) , elabComponentId :: ComponentId , elabInstantiatedWith :: Map ModuleName Module , elabLinkedInstantiatedWith :: Map ModuleName OpenModule diff --git a/cabal-install/src/Distribution/Client/Setup.hs b/cabal-install/src/Distribution/Client/Setup.hs index ad043c68b7c..49476965d57 100644 --- a/cabal-install/src/Distribution/Client/Setup.hs +++ b/cabal-install/src/Distribution/Client/Setup.hs @@ -719,6 +719,7 @@ filterConfigureFlags' flags cabalLibVersion flags_latest { configBytecodeLib = NoFlag , configInstallDirs = (configInstallDirs flags){bytecodelibdir = NoFlag} + , configIUID = NoFlag } flags_3_13_0 = From e81e5289e6f5572d6c1d8f63c421b3169fb64275 Mon Sep 17 00:00:00 2001 From: sorki Date: Fri, 15 May 2026 09:36:22 +0200 Subject: [PATCH 7/9] cabal-install-solver: add InstanceUnitId to grouping key --- .../Solver/Modular/IndexConversion.hs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs b/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs index 04ec19ba6fc..b3235d0dd69 100644 --- a/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs +++ b/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs @@ -68,18 +68,19 @@ convPIs os arch comp constraints sip strfl solveExes iidx sidx = -- merge their sub-libraries and dependencies so we get -- a similar looking package as if it came from repository. groupInstalledSublibs - :: [(PN, I, PInfo)] + :: [(PN, I, InstanceUnitId, PInfo)] -> [(PN, I, PInfo)] groupInstalledSublibs xs = remapPInfoDepsToInstGroups $ M.elems + $ M.map (\(pn, i, _instId, pInfo) -> (pn, i, pInfo)) $ foldl - (\acc x@(pn, I ver _, _) -> + (\acc x@(pn, I ver _, instId, _) -> M.insertWith - (\(_, newI, newInfo) (_, oldI, oldInfo) -> - (pn, mergeIs oldI newI, mergeInfos oldInfo newInfo) + (\(_, newI, newInstId, newInfo) (_, oldI, _oldInstId, oldInfo) -> + (pn, mergeIs oldI newI, newInstId, mergeInfos oldInfo newInfo) ) - (pn, ver) + (pn, ver, instId) x acc ) @@ -138,7 +139,7 @@ groupInstalledSublibs xs = -- | Convert a Cabal installed package index to the simpler, -- more uniform index format of the solver. -convIPI' :: ShadowPkgs -> SI.InstalledPackageIndex -> [(PN, I, PInfo)] +convIPI' :: ShadowPkgs -> SI.InstalledPackageIndex -> [(PN, I, InstanceUnitId, PInfo)] convIPI' (ShadowPkgs sip) idx = -- apply shadowing whenever there are multiple installed packages with -- the same version @@ -149,8 +150,8 @@ convIPI' (ShadowPkgs sip) idx = where -- shadowing is recorded in the package info - shadow (pn, i, PInfo fdeps comps fds _) - | sip = (pn, i, PInfo fdeps comps fds (Just Shadowed)) + shadow (pn, i, instId, PInfo fdeps comps fds _) + | sip = (pn, i, instId, PInfo fdeps comps fds (Just Shadowed)) shadow x = x -- | Extract/recover the package ID from an installed package info, and convert it to a solver's I. @@ -164,11 +165,11 @@ convId ipi = ( pkgName spi convIP :: SI.InstalledPackageIndex -> IPI.InstalledPackageInfo - -> (PN, I, PInfo) + -> (PN, I, InstanceUnitId, PInfo) convIP idx ipi = case traverse (convIPId (DependencyReason pn M.empty S.empty) comp idx) (IPI.depends ipi) of - Left u -> (pn, i, PInfo [] M.empty M.empty (Just (Broken u))) - Right fds -> (pn, i, PInfo fds components M.empty Nothing) + Left u -> (pn, i, IPI.installedInstanceUnitId ipi, PInfo [] M.empty M.empty (Just (Broken u))) + Right fds -> (pn, i, IPI.installedInstanceUnitId ipi, PInfo fds components M.empty Nothing) where components = M.singleton (ExposedLib $ IPI.sourceLibName ipi) From 1d402775c67100cc11ec80fb1895ba3989267070 Mon Sep 17 00:00:00 2001 From: sorki Date: Fri, 15 May 2026 09:39:02 +0200 Subject: [PATCH 8/9] Drop misleading ComponentLocalBuildInfo comment --- Cabal/src/Distribution/Types/ComponentLocalBuildInfo.hs | 1 - 1 file changed, 1 deletion(-) diff --git a/Cabal/src/Distribution/Types/ComponentLocalBuildInfo.hs b/Cabal/src/Distribution/Types/ComponentLocalBuildInfo.hs index 354b2354375..a7880664745 100644 --- a/Cabal/src/Distribution/Types/ComponentLocalBuildInfo.hs +++ b/Cabal/src/Distribution/Types/ComponentLocalBuildInfo.hs @@ -24,7 +24,6 @@ import Distribution.Types.UnitId import qualified Distribution.InstalledPackageInfo as Installed --- | The first five fields are common across all algebraic variants. data ComponentLocalBuildInfo = LibComponentLocalBuildInfo { componentLocalName :: ComponentName From 6de27a095b9c94b8e0bbd8ab324ce496c6f2b46f Mon Sep 17 00:00:00 2001 From: sorki Date: Mon, 18 May 2026 08:39:37 +0200 Subject: [PATCH 9/9] Add changelog entry --- changelog.d/pr-11788.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 changelog.d/pr-11788.md diff --git a/changelog.d/pr-11788.md b/changelog.d/pr-11788.md new file mode 100644 index 00000000000..aef943b1184 --- /dev/null +++ b/changelog.d/pr-11788.md @@ -0,0 +1,11 @@ +--- +synopsis: Group installed multilib packages +packages: [Cabal, Cabal-syntax, cabal-install, cabal-install-solver] +prs: 11788 +--- + +Introduce `InstanceUnitId` (`instance-id`) field for `InstalledPackageInfo` uniquely identifying +group of components. + +Group components in solvers index conversion pass, allowing us to treat installed packages +similar to source packages.