From 591b6eb225b4cd2cfa4d282089fdb33e86bb14ad Mon Sep 17 00:00:00 2001 From: Mike Pilgrem Date: Tue, 2 Jun 2026 23:37:34 +0100 Subject: [PATCH 1/4] Fix #6896 Allow dependency package to have no main library The basic idea is: * The `InstalledLibraryInfo` constructor is modified so that the `GhcPkgId` of a main library is optional. * A `ConfigCacheTypeFlagLibrary` value is, like a `ConfigCacheTypeFlagExecutable` value, based on the package identifier (rather than the 'GhcPkgId' of the main library, which may not be present) * `Stack.SDist` needs to be considered more carefully. * `fetchAndMarkInstalledPackage` allows for the absence of a buildable main library but the presence of sublibraries. An integration test is added. --- src/Stack/Build/Cache.hs | 13 ++--- src/Stack/Build/ExecutePackage.hs | 50 +++++++++---------- src/Stack/Build/Installed.hs | 24 +++++---- src/Stack/SDist.hs | 5 +- src/Stack/Types/Cache.hs | 37 ++++++++------ src/Stack/Types/Installed.hs | 28 +++++++---- src/Stack/Types/Package.hs | 9 ++-- .../tests/6896-dep-with-no-main-lib/Main.hs | 7 +++ .../files/.gitignore | 2 + .../files/myPackageA/package.yaml | 11 ++++ .../files/myPackageA/src/Lib.hs | 1 + .../files/myPackageB/package.yaml | 13 +++++ .../files/myPackageB/sub/Sublib.hs | 1 + .../files/stack.yaml | 5 ++ 14 files changed, 131 insertions(+), 75 deletions(-) create mode 100644 tests/integration/tests/6896-dep-with-no-main-lib/Main.hs create mode 100644 tests/integration/tests/6896-dep-with-no-main-lib/files/.gitignore create mode 100644 tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageA/package.yaml create mode 100644 tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageA/src/Lib.hs create mode 100644 tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageB/package.yaml create mode 100644 tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageB/sub/Sublib.hs create mode 100644 tests/integration/tests/6896-dep-with-no-main-lib/files/stack.yaml diff --git a/src/Stack/Build/Cache.hs b/src/Stack/Build/Cache.hs index 2f5aca0d3e..366ae18b39 100644 --- a/src/Stack/Build/Cache.hs +++ b/src/Stack/Build/Cache.hs @@ -79,9 +79,7 @@ import Stack.Types.EnvConfig ) import Stack.Types.GhcPkgId ( ghcPkgIdString ) import Stack.Types.Installed - ( InstallLocation (..), Installed (..) - , InstalledLibraryInfo (..), foldOnGhcPkgId' - ) + ( InstallLocation (..), Installed (..), foldOnGhcPkgId' ) import Stack.Types.NamedComponent ( NamedComponent (..), componentCachePath ) import Stack.Types.SourceMap ( smRelDir ) @@ -301,12 +299,9 @@ deleteCaches dir = flagCacheKey :: (HasEnvConfig env) => Installed -> RIO env ConfigCacheKey flagCacheKey installed = do installationRoot <- installationRootLocal - case installed of - Library _ installedInfo -> do - let gid = installedInfo.ghcPkgId - pure $ configCacheKey installationRoot (ConfigCacheTypeFlagLibrary gid) - Executable ident -> pure $ - configCacheKey installationRoot (ConfigCacheTypeFlagExecutable ident) + pure $ configCacheKey installationRoot $ case installed of + Library ident _ -> ConfigCacheTypeFlagLibrary ident + Executable ident -> ConfigCacheTypeFlagExecutable ident -- | Loads the Cabal flag cache for the given installed extra-deps. tryGetFlagCache :: diff --git a/src/Stack/Build/ExecutePackage.hs b/src/Stack/Build/ExecutePackage.hs index 674579e97e..f917fb7204 100644 --- a/src/Stack/Build/ExecutePackage.hs +++ b/src/Stack/Build/ExecutePackage.hs @@ -130,7 +130,7 @@ import Stack.Types.GhcPkgId ( GhcPkgId, ghcPkgIdToText ) import Stack.Types.GlobalOpts ( GlobalOpts (..) ) import Stack.Types.Installed ( InstallLocation (..), Installed (..), InstalledMap - , InstalledLibraryInfo (..) + , InstalledLibraryInfo (..), simpleInstalledLib ) import Stack.Types.IsMutable ( IsMutable (..) ) import Stack.Types.NamedComponent @@ -139,8 +139,7 @@ import Stack.Types.NamedComponent ) import Stack.Types.Package ( LocalPackage (..), Package (..), installedPackageToGhcPkgId - , runMemoizedWith, simpleInstalledLib - , toCabalMungedPackageName + , runMemoizedWith, toCabalMungedPackageName ) import Stack.Types.PackageFile ( PackageWarning (..) ) import Stack.Types.Plan @@ -726,27 +725,28 @@ fetchAndMarkInstalledPackage :: -> PackageIdentifier -> RIO env Installed fetchAndMarkInstalledPackage ee taskInstallLocation package pkgId = do - let ghcPkgIdLoader = fetchGhcPkgIdForLib ee taskInstallLocation package.name - -- Only pure the sub-libraries to cache them if we also cache the main - -- library (that is, if it exists) - if hasBuildableMainLibrary package + let hasMainLibrary = hasBuildableMainLibrary package + subLibs = package.subLibraries + if not hasMainLibrary && null subLibs then do - let foldSubLibToMap subLib mapInMonad = do - maybeGhcpkgId <- ghcPkgIdLoader (Just subLib.name) - mapInMonad <&> case maybeGhcpkgId of - Just v -> Map.insert subLib.name v - _ -> id - subLibsPkgIds <- foldComponentToAnotherCollection - package.subLibraries - foldSubLibToMap - mempty - ghcPkgIdLoader Nothing >>= \case - Nothing -> throwM $ Couldn'tFindPkgId package.name - Just ghcPkgId -> pure $ simpleInstalledLib pkgId ghcPkgId subLibsPkgIds - else do - markExeInstalled taskInstallLocation pkgId -- TODO unify somehow - -- with writeFlagCache? + markExeInstalled taskInstallLocation pkgId + -- TODO: Unify the above somehow with writeFlagCache? pure $ Executable pkgId + else do + ghcPkgId <- if hasMainLibrary + then ghcPkgIdLoader Nothing + else pure Nothing + subLibsPkgIds <- + foldComponentToAnotherCollection subLibs foldSubLibToMap mempty + pure $ simpleInstalledLib pkgId ghcPkgId subLibsPkgIds + where + ghcPkgIdLoader = fetchGhcPkgIdForLib ee taskInstallLocation package.name + + foldSubLibToMap subLib mapInMonad = do + maybeGhcpkgId <- ghcPkgIdLoader (Just subLib.name) + mapInMonad <&> case maybeGhcpkgId of + Just v -> Map.insert subLib.name v + _ -> id fetchGhcPkgIdForLib :: (HasTerm env, HasEnvConfig env) @@ -926,7 +926,7 @@ copyPreCompiled ee task pkgId (PrecompiledCache mlib subLibs exes) = do pure $ Just $ case mpkgid of Nothing -> assert False $ Executable pkgId - Just pkgid -> simpleInstalledLib pkgId pkgid mempty + _ -> simpleInstalledLib pkgId mpkgid mempty where bindir = ee.baseConfigOpts.snapInstallRoot bindirSuffix @@ -1067,8 +1067,8 @@ singleTest topts testsToRun ac ee task installedMap = do idMap <- liftIO $ readTVarIO ee.ghcPkgIds pure $ Map.lookup (taskProvides task) idMap let pkgGhcIdList = case installed of - Just (Library _ libInfo) -> [libInfo.ghcPkgId] - _ -> [] + Just (Library _ libInfo) -> maybeToList libInfo.mMainGhcPkgId + _ -> [] -- doctest relies on template-haskell in QuickCheck-based tests thGhcId <- case L.find ((== "template-haskell") . pkgName . (.packageIdent) . snd) diff --git a/src/Stack/Build/Installed.hs b/src/Stack/Build/Installed.hs index 1e2d4d5b45..77c7cd80f1 100644 --- a/src/Stack/Build/Installed.hs +++ b/src/Stack/Build/Installed.hs @@ -287,7 +287,8 @@ toLoadHelper compiler pkgDb dp = LoadHelper if name `Set.member` wiredInPackages compiler then [] else dp.depends - installedLibInfo = InstalledLibraryInfo ghcPkgId (Right <$> dp.license) mempty + installedLibInfo = + InstalledLibraryInfo (Just ghcPkgId) (Right <$> dp.license) mempty toInstallLocation :: PackageDbVariety -> InstallLocation toInstallLocation GlobalDb = Snap @@ -313,23 +314,26 @@ gatherAndTransformSubLoadHelper lh = (_, Library _ existingLibInfo) = ( pLoc , Library pn existingLibInfo - { subLib = Map.union - incomingLibInfo.subLib - existingLibInfo.subLib - , ghcPkgId = if isJust lh.subLibDump - then existingLibInfo.ghcPkgId - else incomingLibInfo.ghcPkgId + { subLib = Map.union incomingLibInfo.subLib existingLibInfo.subLib + , mMainGhcPkgId = + if isJust lh.subLibDump + then existingLibInfo.mMainGhcPkgId + else incomingLibInfo.mMainGhcPkgId } ) onPreviousLoadHelper newVal _oldVal = newVal (key, value) = case lh.subLibDump of Nothing -> (rawPackageName, rawValue) Just sd -> (sd.packageName, updateAsSublib sd <$> rawValue) + -- rawValue should always have a main library: see toLoadHelper. (rawPackageName, rawValue) = lh.pair updateAsSublib sd (Library (PackageIdentifier _sublibMungedPackageName version) libInfo) - = Library - (PackageIdentifier key version) - libInfo { subLib = Map.singleton sd.libraryName libInfo.ghcPkgId } + = case libInfo.mMainGhcPkgId of + Nothing -> + error "gatherAndTransformSubLoadHelper: the impossible happened!" + Just ghcPkgId' -> Library + (PackageIdentifier key version) + libInfo { subLib = Map.singleton sd.libraryName ghcPkgId' } updateAsSublib _ v = v diff --git a/src/Stack/SDist.hs b/src/Stack/SDist.hs index a514573cd2..9699458228 100644 --- a/src/Stack/SDist.hs +++ b/src/Stack/SDist.hs @@ -226,8 +226,9 @@ getSDistTarball mpvpBounds pkgDir = do (installedMap, _globalDumpPkgs, _snapshotDumpPkgs, _localDumpPkgs) <- getInstalled installMap let deps = Map.fromList - [ (pid, libInfo.ghcPkgId) - | (_, Library pid libInfo) <- Map.elems installedMap] + [ (pid, ghcPkgId) + | (_, Library pid (InstalledLibraryInfo (Just ghcPkgId) _ _)) <- Map.elems installedMap + ] prettyInfoL [ flow "Getting the file list for" , style File (fromString pkgFp) <> "." diff --git a/src/Stack/Types/Cache.hs b/src/Stack/Types/Cache.hs index 154b6499b5..f114d51652 100644 --- a/src/Stack/Types/Cache.hs +++ b/src/Stack/Types/Cache.hs @@ -29,14 +29,13 @@ import Database.Persist.Sql ) import Stack.Prelude import Stack.Types.ConfigureOpts ( ConfigureOpts ) -import Stack.Types.GhcPkgId - ( GhcPkgId, ghcPkgIdToText, parseGhcPkgId ) +import Stack.Types.GhcPkgId ( GhcPkgId ) -- | Type representing types of cache in the Stack project SQLite database. data ConfigCacheType = ConfigCacheTypeConfig -- ^ Cabal configuration cache. - | ConfigCacheTypeFlagLibrary GhcPkgId + | ConfigCacheTypeFlagLibrary PackageIdentifier -- ^ Library Cabal flag cache. | ConfigCacheTypeFlagExecutable PackageIdentifier -- ^ Executable Cabal flag cache. @@ -45,25 +44,35 @@ data ConfigCacheType instance PersistField ConfigCacheType where toPersistValue ConfigCacheTypeConfig = PersistText "config" toPersistValue (ConfigCacheTypeFlagLibrary v) = - PersistText $ "lib:" <> ghcPkgIdToText v + PersistText $ "lib:" <> T.pack (packageIdentifierString v) toPersistValue (ConfigCacheTypeFlagExecutable v) = PersistText $ "exe:" <> T.pack (packageIdentifierString v) + fromPersistValue (PersistText t) = fromMaybe (Left $ "Unexpected ConfigCacheType value: " <> t) $ - config <|> fmap lib (T.stripPrefix "lib:" t) <|> - fmap exe (T.stripPrefix "exe:" t) + config + <|> flagCache ConfigCacheTypeFlagLibrary "lib:" + <|> flagCache ConfigCacheTypeFlagExecutable "exe:" where config | t == "config" = Just (Right ConfigCacheTypeConfig) | otherwise = Nothing - lib v = do - ghcPkgId <- mapLeft tshow (parseGhcPkgId v) - Right $ ConfigCacheTypeFlagLibrary ghcPkgId - exe v = do - pkgId <- - maybe (Left $ "Unexpected ConfigCacheType value: " <> t) Right $ - parsePackageIdentifier (T.unpack v) - Right $ ConfigCacheTypeFlagExecutable pkgId + + flagCache :: + (PackageIdentifier -> ConfigCacheType) + -- ^ Constructor + -> Text + -- ^ Prefex + -> Maybe (Either Text ConfigCacheType) + flagCache constructor prefix = + fmap toConfigCacheType (T.stripPrefix prefix t) + where + toConfigCacheType :: Text -> Either Text ConfigCacheType + toConfigCacheType v = do + pkgId <- + maybe (Left $ "Unexpected ConfigCacheType value: " <> t) Right $ + parsePackageIdentifier (T.unpack v) + Right $ constructor pkgId fromPersistValue _ = Left "Unexpected ConfigCacheType type" instance PersistFieldSql ConfigCacheType where diff --git a/src/Stack/Types/Installed.hs b/src/Stack/Types/Installed.hs index ba84f66b1e..84bb89ca46 100644 --- a/src/Stack/Types/Installed.hs +++ b/src/Stack/Types/Installed.hs @@ -101,10 +101,15 @@ type InstallMap = Map PackageName (InstallLocation, Version) -- information about what is installed. type InstalledMap = Map PackageName (InstallLocation, Installed) +-- TODO: This may not be the best type, as it allows invalid values to be +-- represented. data InstalledLibraryInfo = InstalledLibraryInfo - { ghcPkgId :: GhcPkgId + { mMainGhcPkgId :: Maybe GhcPkgId + -- ^ The main library, if present. If absent, there must be one or more + -- installed sublibraries. , license :: Maybe (Either SPDX.License License) , subLib :: Map StackUnqualCompName GhcPkgId + -- ^ If there are no sublibraries, there must be a main library. } deriving (Eq, Show) @@ -119,19 +124,22 @@ data Installed installedLibraryInfoFromGhcPkgId :: GhcPkgId -> InstalledLibraryInfo installedLibraryInfoFromGhcPkgId ghcPkgId = - InstalledLibraryInfo ghcPkgId Nothing mempty + InstalledLibraryInfo (Just ghcPkgId) Nothing mempty simpleInstalledLib :: PackageIdentifier - -> GhcPkgId + -> Maybe GhcPkgId + -- ^ The id of the installed main library, if any. -> Map StackUnqualCompName GhcPkgId + -- ^ The id of any sublibraries. -> Installed simpleInstalledLib pkgIdentifier ghcPkgId = Library pkgIdentifier . InstalledLibraryInfo ghcPkgId Nothing installedToPackageIdOpt :: InstalledLibraryInfo -> [String] installedToPackageIdOpt libInfo = - M.foldr' (iterator (++)) (pure $ toStr libInfo.ghcPkgId) libInfo.subLib + let acc0 = toStr <$> maybeToList libInfo.mMainGhcPkgId + in M.foldr' (iterator (++)) acc0 libInfo.subLib where toStr ghcPkgId = "-package-id=" <> ghcPkgIdString ghcPkgId iterator op ghcPkgId acc = pure (toStr ghcPkgId) `op` acc @@ -141,17 +149,17 @@ installedPackageIdentifier (Library pid _) = pid installedPackageIdentifier (Executable pid) = pid -- | A strict fold over the 'GhcPkgId' of the given installed package. This will --- iterate on both sub and main libraries, if any. +-- iterate on the main library (if any) and sublibraries (if any). foldOnGhcPkgId' :: - (Maybe StackUnqualCompName -> GhcPkgId -> resT -> resT) + (Maybe StackUnqualCompName -> GhcPkgId -> a -> a) -> Installed - -> resT - -> resT + -> a + -> a foldOnGhcPkgId' _ Executable{} res = res foldOnGhcPkgId' fn (Library _ libInfo) res = - M.foldrWithKey' (fn . Just) (base res) libInfo.subLib + M.foldrWithKey' (fn . Just) base libInfo.subLib where - base = fn Nothing libInfo.ghcPkgId + base = maybe res (\ghcPkgId -> fn Nothing ghcPkgId res) libInfo.mMainGhcPkgId -- | Get the installed Version. installedVersion :: Installed -> Version diff --git a/src/Stack/Types/Package.hs b/src/Stack/Types/Package.hs index 488415ddea..42c295166d 100644 --- a/src/Stack/Types/Package.hs +++ b/src/Stack/Types/Package.hs @@ -40,7 +40,6 @@ module Stack.Types.Package , packageIdentifier , psVersion , runMemoizedWith - , simpleInstalledLib , toCabalMungedPackageName , toPackageDbVariety ) where @@ -74,8 +73,7 @@ import Stack.Types.Installed ( InstallLocation (..), InstallMap, Installed (..) , InstalledLibraryInfo (..), InstalledMap , InstalledPackageLocation (..), PackageDatabase (..) - , PackageDbVariety(..), simpleInstalledLib - , toPackageDbVariety + , PackageDbVariety(..), toPackageDbVariety ) import Stack.Types.NamedComponent ( NamedComponent ) import Stack.Types.PackageFile @@ -386,7 +384,8 @@ dotCabalGetPath dcp = DotCabalFilePath fp -> fp DotCabalCFilePath fp -> fp --- | Gathers all the GhcPkgId provided by a library into a map +-- | Gathers all the GhcPkgId provided by a library into a map, where the +-- package identifier of a sublibrary is its munged package identifier. installedMapGhcPkgId :: PackageIdentifier -> InstalledLibraryInfo @@ -394,7 +393,7 @@ installedMapGhcPkgId :: installedMapGhcPkgId pkgId@(PackageIdentifier pkgName version) installedLib = finalMap where - finalMap = M.insert pkgId installedLib.ghcPkgId baseMap + finalMap = maybe id (M.insert pkgId) installedLib.mMainGhcPkgId baseMap baseMap = M.mapKeysMonotonic (toCabalMungedPackageIdentifier pkgName version) diff --git a/tests/integration/tests/6896-dep-with-no-main-lib/Main.hs b/tests/integration/tests/6896-dep-with-no-main-lib/Main.hs new file mode 100644 index 0000000000..36b1ff3ab4 --- /dev/null +++ b/tests/integration/tests/6896-dep-with-no-main-lib/Main.hs @@ -0,0 +1,7 @@ +-- | Stack can support a dependency package that has one or more public +-- sublibraries but no unnamed main library. + +import StackTest + +main :: IO () +main = stack ["build"] diff --git a/tests/integration/tests/6896-dep-with-no-main-lib/files/.gitignore b/tests/integration/tests/6896-dep-with-no-main-lib/files/.gitignore new file mode 100644 index 0000000000..f9a6e152d2 --- /dev/null +++ b/tests/integration/tests/6896-dep-with-no-main-lib/files/.gitignore @@ -0,0 +1,2 @@ +myPackageA.cabal +myPackageB.cabal diff --git a/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageA/package.yaml b/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageA/package.yaml new file mode 100644 index 0000000000..89c90db88c --- /dev/null +++ b/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageA/package.yaml @@ -0,0 +1,11 @@ +spec-version: 0.36.0 + +name: myPackageA + +dependencies: +- base + +library: + source-dirs: src + dependencies: + - myPackageB:myPackageB-sub diff --git a/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageA/src/Lib.hs b/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageA/src/Lib.hs new file mode 100644 index 0000000000..6d85a26fe1 --- /dev/null +++ b/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageA/src/Lib.hs @@ -0,0 +1 @@ +module Lib where diff --git a/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageB/package.yaml b/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageB/package.yaml new file mode 100644 index 0000000000..05fd49c434 --- /dev/null +++ b/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageB/package.yaml @@ -0,0 +1,13 @@ +# This package has no unnamed main library. + +spec-version: 0.36.0 + +name: myPackageB + +dependencies: +- base + +internal-libraries: + myPackageB-sub: + visibility: public + source-dirs: sub diff --git a/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageB/sub/Sublib.hs b/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageB/sub/Sublib.hs new file mode 100644 index 0000000000..c41d4cbf16 --- /dev/null +++ b/tests/integration/tests/6896-dep-with-no-main-lib/files/myPackageB/sub/Sublib.hs @@ -0,0 +1 @@ +module Sublib where diff --git a/tests/integration/tests/6896-dep-with-no-main-lib/files/stack.yaml b/tests/integration/tests/6896-dep-with-no-main-lib/files/stack.yaml new file mode 100644 index 0000000000..68891a8d7c --- /dev/null +++ b/tests/integration/tests/6896-dep-with-no-main-lib/files/stack.yaml @@ -0,0 +1,5 @@ +snapshot: ghc-9.10.3 + +packages: +- myPackageA +- myPackageB From 72ca202a33400646591664a35d606d1b614f5744 Mon Sep 17 00:00:00 2001 From: Mike Pilgrem Date: Thu, 4 Jun 2026 22:01:11 +0100 Subject: [PATCH 2/4] Remove unused licence info from `InstalledLibraryInfo` --- src/Stack/Build/Installed.hs | 3 +-- src/Stack/SDist.hs | 2 +- src/Stack/Types/Installed.hs | 12 ++++-------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Stack/Build/Installed.hs b/src/Stack/Build/Installed.hs index 77c7cd80f1..609175df4d 100644 --- a/src/Stack/Build/Installed.hs +++ b/src/Stack/Build/Installed.hs @@ -287,8 +287,7 @@ toLoadHelper compiler pkgDb dp = LoadHelper if name `Set.member` wiredInPackages compiler then [] else dp.depends - installedLibInfo = - InstalledLibraryInfo (Just ghcPkgId) (Right <$> dp.license) mempty + installedLibInfo = InstalledLibraryInfo (Just ghcPkgId) mempty toInstallLocation :: PackageDbVariety -> InstallLocation toInstallLocation GlobalDb = Snap diff --git a/src/Stack/SDist.hs b/src/Stack/SDist.hs index 9699458228..a30fefbbe7 100644 --- a/src/Stack/SDist.hs +++ b/src/Stack/SDist.hs @@ -227,7 +227,7 @@ getSDistTarball mpvpBounds pkgDir = do getInstalled installMap let deps = Map.fromList [ (pid, ghcPkgId) - | (_, Library pid (InstalledLibraryInfo (Just ghcPkgId) _ _)) <- Map.elems installedMap + | (_, Library pid (InstalledLibraryInfo (Just ghcPkgId) _)) <- Map.elems installedMap ] prettyInfoL [ flow "Getting the file list for" diff --git a/src/Stack/Types/Installed.hs b/src/Stack/Types/Installed.hs index 84bb89ca46..6b7a159207 100644 --- a/src/Stack/Types/Installed.hs +++ b/src/Stack/Types/Installed.hs @@ -28,8 +28,6 @@ module Stack.Types.Installed ) where import qualified Data.Map as M -import qualified Distribution.SPDX.License as SPDX -import Distribution.License ( License ) import Stack.Prelude import Stack.Types.ComponentUtils ( StackUnqualCompName ) import Stack.Types.GhcPkgId ( GhcPkgId, ghcPkgIdString ) @@ -107,7 +105,6 @@ data InstalledLibraryInfo = InstalledLibraryInfo { mMainGhcPkgId :: Maybe GhcPkgId -- ^ The main library, if present. If absent, there must be one or more -- installed sublibraries. - , license :: Maybe (Either SPDX.License License) , subLib :: Map StackUnqualCompName GhcPkgId -- ^ If there are no sublibraries, there must be a main library. } @@ -116,15 +113,14 @@ data InstalledLibraryInfo = InstalledLibraryInfo -- | Type representing information about what is installed. data Installed = Library PackageIdentifier InstalledLibraryInfo - -- ^ A library, including its installed package id and, optionally, its - -- license. + -- ^ A library, including the ids of its installed packages. | Executable PackageIdentifier -- ^ An executable. deriving (Eq, Show) installedLibraryInfoFromGhcPkgId :: GhcPkgId -> InstalledLibraryInfo installedLibraryInfoFromGhcPkgId ghcPkgId = - InstalledLibraryInfo (Just ghcPkgId) Nothing mempty + InstalledLibraryInfo (Just ghcPkgId) mempty simpleInstalledLib :: PackageIdentifier @@ -133,8 +129,8 @@ simpleInstalledLib :: -> Map StackUnqualCompName GhcPkgId -- ^ The id of any sublibraries. -> Installed -simpleInstalledLib pkgIdentifier ghcPkgId = - Library pkgIdentifier . InstalledLibraryInfo ghcPkgId Nothing +simpleInstalledLib pkgIdentifier mMainGhcPkgId = + Library pkgIdentifier . InstalledLibraryInfo mMainGhcPkgId installedToPackageIdOpt :: InstalledLibraryInfo -> [String] installedToPackageIdOpt libInfo = From 7ad02ea402c670a63494803ef0858406d557419a Mon Sep 17 00:00:00 2001 From: Mike Pilgrem Date: Fri, 5 Jun 2026 22:36:42 +0100 Subject: [PATCH 3/4] Use MungedPackageIdentifier for munged package identifiers --- src/Stack/Build/ConstructPlan.hs | 2 +- src/Stack/Build/ExecuteEnv.hs | 88 +++++++++++++++---- src/Stack/Build/ExecutePackage.hs | 14 ++- src/Stack/ConfigureOpts.hs | 18 ++-- src/Stack/SDist.hs | 15 ++-- src/Stack/Types/Build/ConstructPlan.hs | 2 +- src/Stack/Types/Package.hs | 68 ++++++++------ src/Stack/Types/Plan.hs | 4 +- .../tests/6896-custom-setup/Main.hs | 36 ++++++++ .../tests/6896-custom-setup/files/.gitignore | 5 ++ .../files/myPackageA/Setup.hs | 4 + .../files/myPackageA/package.yaml | 13 +++ .../files/myPackageA/src/LibA.hs | 1 + .../files/myPackageB/Setup.hs | 6 ++ .../files/myPackageB/package.yaml | 14 +++ .../files/myPackageB/src/LibB.hs | 1 + .../files/myPackageC/Setup.hs | 6 ++ .../files/myPackageC/package.yaml | 17 ++++ .../files/myPackageC/src/LibC.hs | 1 + .../files/myPackageD/package.yaml | 9 ++ .../files/myPackageD/src/LibD.hs | 6 ++ .../files/myPackageE/package.yaml | 11 +++ .../files/myPackageE/sub/SublibE.hs | 6 ++ .../tests/6896-custom-setup/files/stack.yaml | 8 ++ 24 files changed, 280 insertions(+), 75 deletions(-) create mode 100644 tests/integration/tests/6896-custom-setup/Main.hs create mode 100644 tests/integration/tests/6896-custom-setup/files/.gitignore create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageA/Setup.hs create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageA/package.yaml create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageA/src/LibA.hs create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageB/Setup.hs create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageB/package.yaml create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageB/src/LibB.hs create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageC/Setup.hs create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageC/package.yaml create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageC/src/LibC.hs create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageD/package.yaml create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageD/src/LibD.hs create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageE/package.yaml create mode 100644 tests/integration/tests/6896-custom-setup/files/myPackageE/sub/SublibE.hs create mode 100644 tests/integration/tests/6896-custom-setup/files/stack.yaml diff --git a/src/Stack/Build/ConstructPlan.hs b/src/Stack/Build/ConstructPlan.hs index 37959fe739..ddb92bed59 100644 --- a/src/Stack/Build/ConstructPlan.hs +++ b/src/Stack/Build/ConstructPlan.hs @@ -1081,7 +1081,7 @@ checkDirtiness :: PackageSource -> Installed -> Package - -> Map PackageIdentifier GhcPkgId + -> Map MungedPackageId GhcPkgId -> Bool -- ^ Is Haddock documentation being built? -> M Bool diff --git a/src/Stack/Build/ExecuteEnv.hs b/src/Stack/Build/ExecuteEnv.hs index 89d381f2a1..8114ce5812 100644 --- a/src/Stack/Build/ExecuteEnv.hs +++ b/src/Stack/Build/ExecuteEnv.hs @@ -97,7 +97,8 @@ import Stack.Types.CompilerPaths import Stack.Types.Config ( Config (..), HasConfig (..), stackRootL ) import Stack.Types.ConfigureOpts ( BaseConfigOpts (..) ) -import Stack.Types.Dependency ( DepValue(..) ) +import Stack.Types.Dependency + ( DepLibrary (..), DepType (..), DepValue (..) ) import Stack.Types.DumpLogs ( DumpLogs (..) ) import Stack.Types.DumpPackage ( DumpPackage (..) ) import Stack.Types.EnvConfig @@ -108,7 +109,9 @@ import Stack.Types.EnvSettings ( EnvSettings (..) ) import Stack.Types.GhcPkgId ( GhcPkgId, ghcPkgIdString ) import Stack.Types.Installed ( InstallLocation (..), Installed (..) ) import Stack.Types.Package - ( LocalPackage (..), Package (..), packageIdentifier ) + ( LocalPackage (..), Package (..), packageIdentifier + , toCabalMungedPackageName + ) import Stack.Types.Plan ( TaskType (..), taskTypeLocation, taskTypePackageIdentifier ) @@ -596,7 +599,7 @@ withSingleContext :: => ActionContext -> ExecuteEnv -> TaskType - -> Map PackageIdentifier GhcPkgId + -> Map MungedPackageId GhcPkgId -- ^ Ids of Installed packages that are assumed to be available to build a -- package's custom @Setup.hs@, given its dependencies specified in its -- @custom-setup@ stanza of its Cabal file. @@ -800,25 +803,57 @@ withSingleContext pure cabalPackageArg matchedDeps <- forM (Map.toList customSetupDeps) $ \(name, depValue) -> do - let matches (PackageIdentifier name' version) = - name == name' + let mungedPkgNames = depToMungedPkgNames name depValue + countMungedPkgNames = Set.size mungedPkgNames + matches (MungedPackageId mungedPkgName version) _ = + mungedPkgName `Set.member` mungedPkgNames && version `withinRange` depValue.versionRange - case filter (matches . fst) (Map.toList allDeps) of - x:xs -> do - unless (null xs) $ - prettyWarnL - [ flow "Found multiple installed packages for \ - \custom-setup dep:" - , style Current (fromPackageName name) <> "." - ] - pure ("-package-id=" ++ ghcPkgIdString (snd x), Just (fst x)) - [] -> do + case Map.filterWithKey matches allDeps of + matchedDeps | Map.null matchedDeps -> do prettyWarnL [ flow "Could not find custom-setup dep:" , style Current (fromPackageName name) <> "." ] - pure ("-package=" ++ packageNameString name, Nothing) - let depsArgs = map fst matchedDeps + pure (["-package=" <> packageNameString name], Nothing) + matchedDeps -> do + let groupMatchedByVersion = + Map.foldlWithKey' + ( \acc k v -> + let p = mungedVersion k + innerMap = Map.singleton k v + in Map.insertWith Map.union p innerMap acc + ) + Map.empty + matchedDeps + countMatchedDeps = Map.size matchedDeps + if Map.size groupMatchedByVersion == 1 + then do + when (countMatchedDeps < countMungedPkgNames) $ + prettyWarnL + [ flow "Found insufficent installed packages \ + \for custom-setup dep:" + , style Current (fromPackageName name) <> "." + ] + else do + prettyWarnL + [ flow "Found installed packages with multiple \ + \versions for custom-setup dep:" + , style Current (fromPackageName name) <> "." + ] + let packageIdOpt ghcPkgId = + "-package-id=" <> ghcPkgIdString ghcPkgId + -- The previous algorithm (arbitrarily?) selected + -- the first relevant item yielded by Map.toList + -- (which is Map.toAscList), so we select the + -- minimum: + selectedGroup = Map.findMin groupMatchedByVersion + selectedVersion = fst selectedGroup + packageIdOpts = + map packageIdOpt $ Map.elems $ snd selectedGroup + selectedPkgId = + PackageIdentifier name selectedVersion + pure (packageIdOpts, Just selectedPkgId) + let depsArgs = L.concatMap fst matchedDeps -- Generate setup_macros.h and provide it to ghc let macroDeps = mapMaybe snd matchedDeps cppMacrosFile = setupDir relFileSetupMacrosH @@ -880,6 +915,25 @@ withSingleContext setupArgs = ("--builddir=" ++ toFilePathNoTrailingSep distRelativeDir') : args + depToMungedPkgNames :: + PackageName + -- ^ The name of the Cabal package. + -> DepValue + -- ^ The dependency value for that package. + -> Set.Set MungedPackageName + depToMungedPkgNames pkgName depValue + | AsLibrary depLibrary <- depValue.depType = + let addMain = if depLibrary.main + then Set.insert mungedMainPkgName + else id + mungedMainPkgName = toCabalMungedPackageName pkgName Nothing + subLibSet = + Set.map + (toCabalMungedPackageName pkgName . Just) + depLibrary.subLib + in addMain subLibSet + | otherwise = Set.empty + runExe :: Path Abs File -> [String] -> RIO env () runExe exeName fullArgs = do runAndOutput `catch` \ece -> do diff --git a/src/Stack/Build/ExecutePackage.hs b/src/Stack/Build/ExecutePackage.hs index f917fb7204..233a9a56b9 100644 --- a/src/Stack/Build/ExecutePackage.hs +++ b/src/Stack/Build/ExecutePackage.hs @@ -163,7 +163,7 @@ getConfigCache :: -> InstalledMap -> Bool -> Bool - -> RIO env (Map PackageIdentifier GhcPkgId, ConfigCache) + -> RIO env (Map MungedPackageId GhcPkgId, ConfigCache) getConfigCache ee task installedMap enableTest enableBench = do let extra = -- We enable tests if the test suite dependencies are already @@ -424,7 +424,7 @@ realConfigAndBuild :: -- ^ (isFinalBuild, buildingFinals) -> ConfigCache -> Maybe Curator - -> Map PackageIdentifier GhcPkgId + -> Map MungedPackageId GhcPkgId -- ^ Ids of installed packages that are assumed to be available to build a -- package's custom @Setup.hs@, given its dependencies specified in its -- @custom-setup@ stanza of its Cabal file. @@ -755,7 +755,7 @@ fetchGhcPkgIdForLib :: -> PackageName -> Maybe Component.StackUnqualCompName -> RIO env (Maybe GhcPkgId) -fetchGhcPkgIdForLib ee installLocation pkgName libName = do +fetchGhcPkgIdForLib ee installLocation pkgName mLibName = do let baseConfigOpts = ee.baseConfigOpts (installedPkgDb, installedDumpPkgsTVar) = case installLocation of @@ -766,11 +766,9 @@ fetchGhcPkgIdForLib ee installLocation pkgName libName = do ( baseConfigOpts.localDB , ee.localDumpPkgs ) let commonLoader = loadInstalledPkg [installedPkgDb] installedDumpPkgsTVar - case libName of - Nothing -> commonLoader pkgName - Just v -> do - let mungedName = encodeCompatPackageName $ toCabalMungedPackageName pkgName v - commonLoader mungedName + mungedPkgName = toCabalMungedPackageName pkgName mLibName + encodedPkgName = encodeCompatPackageName mungedPkgName + commonLoader encodedPkgName -- | Copy ddump-* files, if we are building finals and a non-empty ddump-dir -- has been specified. diff --git a/src/Stack/ConfigureOpts.hs b/src/Stack/ConfigureOpts.hs index 539d12ddcc..6a31ed3740 100644 --- a/src/Stack/ConfigureOpts.hs +++ b/src/Stack/ConfigureOpts.hs @@ -18,8 +18,6 @@ module Stack.ConfigureOpts import qualified Data.Map as Map import qualified Data.Text as T import Database.Persist ( Entity, entityVal ) -import Distribution.Types.MungedPackageName - ( decodeCompatPackageName ) import Distribution.Types.PackageName ( unPackageName ) import Distribution.Types.UnqualComponentName ( unUnqualComponentName ) @@ -70,7 +68,7 @@ configureOptsFromDb x y = ConfigureOpts configureOptsFromBase :: EnvConfig -> BaseConfigOpts - -> Map PackageIdentifier GhcPkgId -- ^ dependencies + -> Map MungedPackageId GhcPkgId -- ^ dependencies -> Bool -- ^ local non-extra-dep? -> IsMutable -> PackageConfigureOpts @@ -119,7 +117,7 @@ configureOptsPathRelated bco isMutable pkgOpts = concat configureOptsNonPathRelated :: EnvConfig -> BaseConfigOpts - -> Map PackageIdentifier GhcPkgId -- ^ Dependencies. + -> Map MungedPackageId GhcPkgId -- ^ Dependencies. -> Bool -- ^ Is this a local, non-extra-dep? -> PackageConfigureOpts -> [String] @@ -197,18 +195,18 @@ configureOptsNonPathRelated econfig bco deps isLocal package = concat depOptions = mapAndAppend toDepOption [] deps - toDepOption (PackageIdentifier name _) gid = concat + toDepOption (MungedPackageId name _) gid = concat [ "--dependency=" , depOptionKey , "=" , ghcPkgIdString gid ] where - MungedPackageName subPkgName lib = decodeCompatPackageName name - depOptionKey = case lib of - LMainLibName -> unPackageName name - LSubLibName cn -> - unPackageName subPkgName <> ":" <> unUnqualComponentName cn + MungedPackageName pkgName libName = name + pkgName' = unPackageName pkgName + depOptionKey = case libName of + LMainLibName -> pkgName' + LSubLibName cn -> pkgName' <> ":" <> unUnqualComponentName cn -- | Render configure options as a single list of options. renderConfigureOpts :: ConfigureOpts -> [String] diff --git a/src/Stack/SDist.hs b/src/Stack/SDist.hs index a30fefbbe7..e018b39def 100644 --- a/src/Stack/SDist.hs +++ b/src/Stack/SDist.hs @@ -81,12 +81,11 @@ import Stack.Types.EnvConfig ( EnvConfig (..), HasEnvConfig (..), actualCompilerVersionL ) import Stack.Types.GhcPkgId ( GhcPkgId ) import Stack.Types.Installed - ( InstallMap, Installed (..), InstalledMap - , InstalledLibraryInfo (..), installedVersion + ( InstallMap, InstalledMap, installedVersion ) import Stack.Types.Package ( LocalPackage (..), Package (..), PackageConfig (..) - , packageIdentifier + , installedPackageToGhcPkgId', packageIdentifier ) import Stack.Types.Plan ( TaskType (..) ) import Stack.Types.Platform ( HasPlatform (..) ) @@ -225,15 +224,13 @@ getSDistTarball mpvpBounds pkgDir = do installMap <- toInstallMap sourceMap (installedMap, _globalDumpPkgs, _snapshotDumpPkgs, _localDumpPkgs) <- getInstalled installMap - let deps = Map.fromList - [ (pid, ghcPkgId) - | (_, Library pid (InstalledLibraryInfo (Just ghcPkgId) _)) <- Map.elems installedMap - ] + let allDeps = + Map.unions $ Map.map (installedPackageToGhcPkgId' . snd) installedMap prettyInfoL [ flow "Getting the file list for" , style File (fromString pkgFp) <> "." ] - (fileList, cabalFP) <- getSDistFileList lp deps + (fileList, cabalFP) <- getSDistFileList lp allDeps prettyInfoL [ flow "Building a compressed archive file in the sdist format for" , style File (fromString pkgFp) <> "." @@ -477,7 +474,7 @@ readLocalPackage pkgDir = do getSDistFileList :: HasEnvConfig env => LocalPackage - -> Map PackageIdentifier GhcPkgId + -> Map MungedPackageId GhcPkgId -- ^ Ids of installed packages that are assumed to be available to build a -- package's custom @Setup.hs@, given its dependencies specified in its -- @custom-setup@ stanza of its Cabal file. diff --git a/src/Stack/Types/Build/ConstructPlan.hs b/src/Stack/Types/Build/ConstructPlan.hs index 35a6c8b9a9..3f06cda318 100644 --- a/src/Stack/Types/Build/ConstructPlan.hs +++ b/src/Stack/Types/Build/ConstructPlan.hs @@ -154,7 +154,7 @@ adrHasLibrary (ADRFound _ Executable{}) = False data MissingPresentDeps = MissingPresentDeps { missingPackages :: !(Set PackageIdentifier) - , presentPackages :: !(Map PackageIdentifier GhcPkgId) + , presentPackages :: !(Map MungedPackageId GhcPkgId) , isMutable :: !IsMutable } deriving Show diff --git a/src/Stack/Types/Package.hs b/src/Stack/Types/Package.hs index 42c295166d..a316fb397d 100644 --- a/src/Stack/Types/Package.hs +++ b/src/Stack/Types/Package.hs @@ -33,6 +33,7 @@ module Stack.Types.Package , dotCabalModulePath , installedMapGhcPkgId , installedPackageToGhcPkgId + , installedPackageToGhcPkgId' , lpFiles , lpFilesForComponents , memoizeRefWith @@ -40,6 +41,7 @@ module Stack.Types.Package , packageIdentifier , psVersion , runMemoizedWith + , toCabalMungedPackageId , toCabalMungedPackageName , toPackageDbVariety ) where @@ -53,8 +55,6 @@ import Distribution.License ( License ) import Distribution.ModuleName ( ModuleName ) import Distribution.PackageDescription ( BuildType ) import Distribution.System ( Platform (..) ) -import Distribution.Types.MungedPackageName - ( encodeCompatPackageName ) import qualified RIO.Text as T import Stack.Prelude import Stack.Types.Cache ( FileCache ) @@ -73,7 +73,8 @@ import Stack.Types.Installed ( InstallLocation (..), InstallMap, Installed (..) , InstalledLibraryInfo (..), InstalledMap , InstalledPackageLocation (..), PackageDatabase (..) - , PackageDbVariety(..), toPackageDbVariety + , PackageDbVariety(..), installedPackageIdentifier + , toPackageDbVariety ) import Stack.Types.NamedComponent ( NamedComponent ) import Stack.Types.PackageFile @@ -388,40 +389,53 @@ dotCabalGetPath dcp = -- package identifier of a sublibrary is its munged package identifier. installedMapGhcPkgId :: PackageIdentifier + -- ^ The name and version of a Cabal package. -> InstalledLibraryInfo - -> Map PackageIdentifier GhcPkgId -installedMapGhcPkgId pkgId@(PackageIdentifier pkgName version) installedLib = - finalMap + -> Map MungedPackageId GhcPkgId +installedMapGhcPkgId pkgId libInfo = + mAddMainGhcPkgId libInfo.mMainGhcPkgId subLibMap where - finalMap = maybe id (M.insert pkgId) installedLib.mMainGhcPkgId baseMap - baseMap = - M.mapKeysMonotonic - (toCabalMungedPackageIdentifier pkgName version) - installedLib.subLib + mAddMainGhcPkgId = maybe id (M.insert mungedMainPkgId) + mungedMainPkgId = toCabalMungedPackageId pkgId Nothing + subLibMap = + M.mapKeysMonotonic (toCabalMungedPackageId pkgId . Just) libInfo.subLib installedPackageToGhcPkgId :: PackageIdentifier + -- ^ The name and version of a Cabal package. -> Installed - -> Map PackageIdentifier GhcPkgId -installedPackageToGhcPkgId ident (Library ident' libInfo) = - assert (ident == ident') (installedMapGhcPkgId ident libInfo) -installedPackageToGhcPkgId _ (Executable _) = mempty - --- | Creates a t'MungedPackageName' identifier. -toCabalMungedPackageIdentifier :: - PackageName - -> Version - -> StackUnqualCompName - -> PackageIdentifier -toCabalMungedPackageIdentifier pkgName version = flip PackageIdentifier version - . encodeCompatPackageName . toCabalMungedPackageName pkgName + -- ^ Assumed to be for the same Cabal package name and version. + -> Map MungedPackageId GhcPkgId +installedPackageToGhcPkgId pkgId installed = + assert (pkgId == installedPackageIdentifier installed) + (installedPackageToGhcPkgId' installed) + +installedPackageToGhcPkgId' :: Installed -> Map MungedPackageId GhcPkgId +installedPackageToGhcPkgId' (Library pkgId libInfo) = + installedMapGhcPkgId pkgId libInfo +installedPackageToGhcPkgId' (Executable _) = mempty + +-- | Creates a munged package identifier. +toCabalMungedPackageId :: + PackageIdentifier + -- ^ The name and version of a Cabal package. + -> Maybe StackUnqualCompName + -- ^ 'Nothing', if a main library. + -> MungedPackageId +toCabalMungedPackageId pkgId mLibName = + MungedPackageId (toCabalMungedPackageName pkgName mLibName) version + where + PackageIdentifier pkgName version = pkgId +-- | Creates a munged package name. toCabalMungedPackageName :: PackageName - -> StackUnqualCompName + -- ^ The name of a Cabal package. + -> Maybe StackUnqualCompName + -- ^ 'Nothing', if a main library. -> MungedPackageName -toCabalMungedPackageName pkgName = - MungedPackageName pkgName . LSubLibName . toCabalName +toCabalMungedPackageName pkgName mLibName = MungedPackageName pkgName $ + maybe LMainLibName (LSubLibName . toCabalName) mLibName -- | Type representing inputs to 'Stack.Package.generateBuildInfoOpts'. data BioInput = BioInput diff --git a/src/Stack/Types/Plan.hs b/src/Stack/Types/Plan.hs index 8789f3f0d9..3a10ea02a3 100644 --- a/src/Stack/Types/Plan.hs +++ b/src/Stack/Types/Plan.hs @@ -63,8 +63,8 @@ data Task = Task -- are missing and a function which yields configure options, given a -- dictionary of those identifiers and their 'GhcPkgId'. , buildHaddocks :: !Bool - , present :: !(Map PackageIdentifier GhcPkgId) - -- ^ A dictionary of the package identifiers of already-installed + , present :: !(Map MungedPackageId GhcPkgId) + -- ^ A dictionary of the munged package identifiers of already-installed -- dependencies, and their 'GhcPkgId'. , allInOne :: !Bool -- ^ indicates that the package can be built in one step diff --git a/tests/integration/tests/6896-custom-setup/Main.hs b/tests/integration/tests/6896-custom-setup/Main.hs new file mode 100644 index 0000000000..18cdffa792 --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/Main.hs @@ -0,0 +1,36 @@ +-- | Stack can build: +-- +-- * a project package with a custom @Setup.hs@ (see @myPackageA@); +-- +-- * a project package with a custom @Setup.hs@ that depends on the main library +-- of another project package (see @myPackageB@ and @myPackageD@); and +-- +-- * a project package with a custom @Setup.hs@ that depends on the public +-- sublibrary of another project package that does not have a main library +-- (see @myPackageC@ amd @myPackageE@). +-- +-- See: https://github.com/commercialhaskell/stack/issues/6896 + +import Control.Monad ( unless) +import Data.List ( isInfixOf ) +import StackTest + +main :: IO () +main = do + stackCheckStderr ["build", "myPackageA"] (expectMessage usingCustomA) + stackCheckStderr ["build", "myPackageB"] (expectMessage usingCustomB) + stackCheckStderr ["build", "myPackageC"] (expectMessage usingCustomC) + +usingCustomA :: String +usingCustomA = "Using my custom Setup.hs for myPackageA" + +usingCustomB :: String +usingCustomB = "messageD: Using my custom Setup.hs for myPackageB" + +usingCustomC :: String +usingCustomC = "messageE: Using my custom Setup.hs for myPackageC" + +expectMessage :: String -> String -> IO () +expectMessage msg stderr = do + unless (words msg `isInfixOf` words stderr) $ + error $ "Expected output: \n" ++ show msg diff --git a/tests/integration/tests/6896-custom-setup/files/.gitignore b/tests/integration/tests/6896-custom-setup/files/.gitignore new file mode 100644 index 0000000000..ec90d3285d --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/.gitignore @@ -0,0 +1,5 @@ +myPackageA.cabal +myPackageB.cabal +myPackageC.cabal +myPackageD.cabal +myPackageE.cabal diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageA/Setup.hs b/tests/integration/tests/6896-custom-setup/files/myPackageA/Setup.hs new file mode 100644 index 0000000000..dcc59c17b5 --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageA/Setup.hs @@ -0,0 +1,4 @@ +import Distribution.Simple +main = do + putStrLn "Using my custom Setup.hs for myPackageA" + defaultMain diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageA/package.yaml b/tests/integration/tests/6896-custom-setup/files/myPackageA/package.yaml new file mode 100644 index 0000000000..4cdfea8748 --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageA/package.yaml @@ -0,0 +1,13 @@ +spec-version: 0.36.0 + +name: myPackageA + +dependencies: +- base + +custom-setup: + dependencies: + - base + +library: + source-dirs: src diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageA/src/LibA.hs b/tests/integration/tests/6896-custom-setup/files/myPackageA/src/LibA.hs new file mode 100644 index 0000000000..a02dbb2486 --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageA/src/LibA.hs @@ -0,0 +1 @@ +module LibA where diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageB/Setup.hs b/tests/integration/tests/6896-custom-setup/files/myPackageB/Setup.hs new file mode 100644 index 0000000000..d1b4ae8498 --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageB/Setup.hs @@ -0,0 +1,6 @@ +import Distribution.Simple +import LibD ( messageD ) + +main = do + putStrLn messageD + defaultMain diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageB/package.yaml b/tests/integration/tests/6896-custom-setup/files/myPackageB/package.yaml new file mode 100644 index 0000000000..06acaf9aac --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageB/package.yaml @@ -0,0 +1,14 @@ +spec-version: 0.36.0 + +name: myPackageB + +dependencies: +- base + +custom-setup: + dependencies: + - base + - myPackageD + +library: + source-dirs: src diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageB/src/LibB.hs b/tests/integration/tests/6896-custom-setup/files/myPackageB/src/LibB.hs new file mode 100644 index 0000000000..913d829c0a --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageB/src/LibB.hs @@ -0,0 +1 @@ +module LibB where diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageC/Setup.hs b/tests/integration/tests/6896-custom-setup/files/myPackageC/Setup.hs new file mode 100644 index 0000000000..282159a3b8 --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageC/Setup.hs @@ -0,0 +1,6 @@ +import Distribution.Simple +import SublibE ( messageE ) + +main = do + putStrLn messageE + defaultMain diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageC/package.yaml b/tests/integration/tests/6896-custom-setup/files/myPackageC/package.yaml new file mode 100644 index 0000000000..0f504d627a --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageC/package.yaml @@ -0,0 +1,17 @@ +spec-version: 0.36.0 + +verbatim: + cabal-version: 3.4 + +name: myPackageC + +dependencies: +- base + +custom-setup: + dependencies: + - base + - myPackageE:myPackageE-sub + +library: + source-dirs: src diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageC/src/LibC.hs b/tests/integration/tests/6896-custom-setup/files/myPackageC/src/LibC.hs new file mode 100644 index 0000000000..48da7c9108 --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageC/src/LibC.hs @@ -0,0 +1 @@ +module LibC where diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageD/package.yaml b/tests/integration/tests/6896-custom-setup/files/myPackageD/package.yaml new file mode 100644 index 0000000000..3d51f11ae8 --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageD/package.yaml @@ -0,0 +1,9 @@ +spec-version: 0.36.0 + +name: myPackageD + +dependencies: +- base + +library: + source-dirs: src diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageD/src/LibD.hs b/tests/integration/tests/6896-custom-setup/files/myPackageD/src/LibD.hs new file mode 100644 index 0000000000..19c7c8dead --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageD/src/LibD.hs @@ -0,0 +1,6 @@ +module LibD + ( messageD + ) where + +messageD :: String +messageD = "messageD: Using my custom Setup.hs for myPackageB" diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageE/package.yaml b/tests/integration/tests/6896-custom-setup/files/myPackageE/package.yaml new file mode 100644 index 0000000000..1edcb2d54d --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageE/package.yaml @@ -0,0 +1,11 @@ +spec-version: 0.36.0 + +name: myPackageE + +dependencies: +- base + +internal-libraries: + myPackageE-sub: + visibility: public + source-dirs: sub diff --git a/tests/integration/tests/6896-custom-setup/files/myPackageE/sub/SublibE.hs b/tests/integration/tests/6896-custom-setup/files/myPackageE/sub/SublibE.hs new file mode 100644 index 0000000000..34f4adc5ec --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/myPackageE/sub/SublibE.hs @@ -0,0 +1,6 @@ +module SublibE + ( messageE + ) where + +messageE :: String +messageE = "messageE: Using my custom Setup.hs for myPackageC" diff --git a/tests/integration/tests/6896-custom-setup/files/stack.yaml b/tests/integration/tests/6896-custom-setup/files/stack.yaml new file mode 100644 index 0000000000..f5e344c0bf --- /dev/null +++ b/tests/integration/tests/6896-custom-setup/files/stack.yaml @@ -0,0 +1,8 @@ +snapshot: ghc-9.10.3 + +packages: +- myPackageA +- myPackageB +- myPackageC +- myPackageD +- myPackageE From a7f865988b5cffad9f8c9632ada3fc21125f22cc Mon Sep 17 00:00:00 2001 From: Mike Pilgrem Date: Wed, 3 Jun 2026 22:02:41 +0100 Subject: [PATCH 4/4] Update STAN configuration --- .stan.toml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.stan.toml b/.stan.toml index a04fef1636..299b627745 100644 --- a/.stan.toml +++ b/.stan.toml @@ -140,25 +140,25 @@ # Anti-pattern: Data.ByteString.Char8.pack [[ignore]] - id = "OBS-STAN-0203-erw24B-1084:3" + id = "OBS-STAN-0203-erw24B-1138:3" # ✦ Description: Usage of 'pack' function that doesn't handle Unicode characters # ✦ Category: #AntiPattern # ✦ File: src\Stack\Build\ExecuteEnv.hs # -# 1083 ┃ -# 1084 ┃ S8.pack . formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%S%6Q" -# 1085 ┃ ^^^^^^^ +# 1137 ┃ +# 1138 ┃ S8.pack . formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%S%6Q" +# 1139 ┃ ^^^^^^^ # Anti-pattern: Data.ByteString.Char8.pack [[ignore]] - id = "OBS-STAN-0203-tuE+RG-253:24" + id = "OBS-STAN-0203-tuE+RG-252:24" # ✦ Description: Usage of 'pack' function that doesn't handle Unicode characters # ✦ Category: #AntiPattern # ✦ File: src\Stack\Build\ExecutePackage.hs # -# 252 ┃ -# 253 ┃ newConfigFileRoot <- S8.pack . toFilePath <$> view configFileRootL -# 254 ┃ ^^^^^^^ +# 251 ┃ +# 252 ┃ newConfigFileRoot <- S8.pack . toFilePath <$> view configFileRootL +# 253 ┃ ^^^^^^^ # Anti-pattern: Data.ByteString.Char8.pack [[ignore]]