diff --git a/src/Stack/Build/Backpack.hs b/src/Stack/Build/Backpack.hs index 28e11f7b4b..c19fdcbf9a 100644 --- a/src/Stack/Build/Backpack.hs +++ b/src/Stack/Build/Backpack.hs @@ -16,13 +16,12 @@ tasks that instantiate those packages with concrete implementations. module Stack.Build.Backpack ( addInstantiationTasks - , upgradeFoundIndefinites + , loadFoundIndefiniteTasks ) where import Crypto.Hash ( hashWith, SHA256 (..) ) import qualified Data.ByteArray.Encoding as Mem ( Base (Base16), convertToBase ) -import qualified Data.ByteString.Char8 as S8 import qualified Data.List as L import qualified Data.Map.Strict as Map import qualified Data.Set as Set @@ -37,10 +36,12 @@ import Distribution.Types.ModuleRenaming import Path ( parent ) import Stack.ConfigureOpts ( packageConfigureOptsFromPackage ) import Stack.Package - ( packageIsIndefinite ) + ( packageIsIndefinite, processPackageDepsEither ) import Stack.Prelude import Stack.Types.Build.ConstructPlan - ( AddDepRes (..), CombinedMap, PackageInfo (..) ) + ( AddDepRes (..), CombinedMap, MissingPresentDeps (..) + , PackageInfo (..), processAdr + ) import Stack.Types.SourceMap ( CommonPackage (..) ) import Distribution.Types.BuildType ( BuildType (Configure) ) import Stack.Types.Cache ( CachePkgSrc (..) ) @@ -56,22 +57,29 @@ import Stack.Types.IsMutable ( IsMutable (..) ) import Stack.Types.NamedComponent ( NamedComponent (..) ) import Stack.Types.Package ( LocalPackage (..), Package (..), PackageSource (..) - , installedMapGhcPkgId, packageIdentifier - , toCabalMungedPackageId + , packageIdentifier, psVersion, toCabalMungedPackageId ) import Stack.Types.Plan ( ComponentKey (..), Task (..), TaskConfigOpts (..) , TaskType (..), installLocationIsMutable ) --- | Upgrade ADRFound entries to ADRToInstall for indefinite (Backpack --- signature) packages whose source is available. This is needed because --- addInstantiationTasks clones the sig-pkg's Task to create CInst tasks, --- and ADRFound entries don't carry a Task. +-- | Extract the package metadata from a build task. +taskPackage :: Task -> Package +taskPackage t = case t.taskType of + TTLocalMutable lp -> lp.package + TTRemotePackage _ p _ -> p + +-- | Load source-backed template tasks for ADRFound indefinite (Backpack +-- signature) packages. These tasks are used only as sources for CInst tasks. +-- The original ADRFound entries must remain ADRFound: a clean installed +-- indefinite package should not be added to the build plan just because a +-- dependent package needs a CInst task. -- --- Only packages actually referenced by a consumer's mixin are loaded, --- to avoid unnecessary Pantry lookups for the common case (no Backpack). -upgradeFoundIndefinites :: +-- Only packages actually referenced by a consumer's mixin, plus the indefinite +-- dependencies discovered while loading those package descriptions, are loaded. +-- This avoids unnecessary Pantry lookups for the common case (no Backpack). +loadFoundIndefiniteTasks :: forall env. HasEnvConfig env => ( PackageLocationImmutable -> Map FlagName Bool @@ -81,83 +89,103 @@ upgradeFoundIndefinites :: ) -- ^ Load package from source (loadPackage0) -> EnvConfig + -> Map PackageName PackageSource -> CombinedMap -> BaseConfigOpts -> [(PackageName, AddDepRes)] - -> RIO env [(PackageName, AddDepRes)] -upgradeFoundIndefinites loadPkg econfig combinedMap bco adrs = do - let adrMap = Map.fromList adrs - -- Collect package names referenced by any consumer's mixin. - mixinTargets :: Set PackageName - mixinTargets = Set.fromList - [ mixinPackageName mixin - | (_, ADRToInstall task) <- adrs - , let pkg = case task.taskType of - TTLocalMutable lp -> lp.package - TTRemotePackage _ p _ -> p - , mixin <- concatMap (\lib -> lib.buildInfo.mixins) - (maybeToList pkg.library ++ toList pkg.subLibraries) - ] - -- Filter to mixin targets that are ADRFound. - foundTargets = - [ name - | name <- Set.toList mixinTargets - , Just (ADRFound _ _) <- [Map.lookup name adrMap] - ] - if null foundTargets - then pure adrs - else do - -- For each ADRFound mixin target, try to load its Package and - -- upgrade to ADRToInstall if it is indefinite. - upgrades <- fmap Map.fromList $ forM foundTargets $ \name -> + -> RIO env (Map PackageName Task) +loadFoundIndefiniteTasks loadPkg econfig sources combinedMap bco adrs = + go Set.empty Map.empty foundTargets + where + adrMap :: Map PackageName AddDepRes + adrMap = Map.fromList adrs + + -- Collect package names referenced by any consumer's mixin. + mixinTargets :: Set PackageName + mixinTargets = Set.fromList + [ depName + | (_, ADRToInstall task) <- adrs + , let pkg = taskPackage task + , depName <- mixinTargetsFor pkg + ] + + -- Filter to mixin targets that are ADRFound. + foundTargets :: [PackageName] + foundTargets = + [ name + | name <- Set.toList mixinTargets + , Just (ADRFound _ _) <- [Map.lookup name adrMap] + ] + + go :: + Set PackageName + -> Map PackageName Task + -> [PackageName] + -> RIO env (Map PackageName Task) + go _seen templates [] = pure templates + go seen templates (name:rest) + | name `Set.member` seen = go seen templates rest + | otherwise = do + mTemplate <- loadTemplate name + case mTemplate of + Nothing -> + go (Set.insert name seen) templates rest + Just (_, template) -> + let pkg = taskPackage template + in go + (Set.insert name seen) + (Map.insert name template templates) + (foundDependencyTargets pkg ++ rest) + + loadTemplate :: + PackageName + -> RIO env (Maybe (PackageName, Task)) + loadTemplate name = + case Map.lookup name sources of + Just ps -> + loadFromSourceTemplate name ps + Nothing -> case Map.lookup name combinedMap of Just (PIOnlyInstalled _ installed) -> - -- Source not available (GHC boot library or similar). Look up - -- Hackage as a fallback. - upgradeFromHackage name installed - Just (PIBoth ps installed) -> - upgradeFromSource name ps (Just installed) + -- Source not available from the source map (GHC boot library, + -- external package DB, or similar). Look up Hackage as a fallback; + -- packageIsIndefinite filters out ordinary dependencies. + loadFromHackage name installed + Just (PIBoth ps _) -> + loadFromSourceTemplate name ps Just (PIOnlySource ps) -> -- Should not happen (ADRFound implies installed), but handle -- gracefully by loading the package to check. - upgradeFromSource name ps Nothing + loadFromSourceTemplate name ps Nothing -> -- Package not in combined map — shouldn't happen. Skip. - pure (name, Nothing) - -- Apply upgrades. - pure - [ case Map.lookup name upgrades of - Just (Just adr') -> (name, adr') - _ -> (name, adr) - | (name, adr) <- adrs - ] - where + pure Nothing + loadFromSource :: PackageSource -> RIO env Package loadFromSource = \case PSRemote pkgLoc _version _fromSnapshot cp -> runRIO econfig $ loadPkg pkgLoc cp.flags cp.ghcOptions cp.cabalConfigOpts PSFilePath lp -> pure lp.package - mkTask :: Package -> PackageSource -> Maybe Installed -> Task - mkTask package ps mInstalled = + mkTask :: Package -> PackageSource -> Task + mkTask package ps = let loc = psLocation ps isMutable = installLocationIsMutable loc - presentMap = case mInstalled of - Just (Library pid ili) -> installedMapGhcPkgId pid ili - _ -> Map.empty + MissingPresentDeps missing present minMutable = + templateDeps package in Task { configOpts = TaskConfigOpts - { missing = Set.empty + { missing , envConfig = econfig , baseConfigOpts = bco , isLocalNonExtraDep = psLocal ps - , isMutable + , isMutable = isMutable <> minMutable , pkgConfigOpts = packageConfigureOptsFromPackage package , instantiationDeps = [] } , buildHaddocks = False , allInOne = True - , present = presentMap + , present , taskType = case ps of PSFilePath lp -> TTLocalMutable lp PSRemote pkgLoc _version _fromSnapshot _cp -> @@ -167,22 +195,21 @@ upgradeFoundIndefinites loadPkg econfig combinedMap bco adrs = do , backpackInstEntries = [] } - upgradeFromSource :: + loadFromSourceTemplate :: PackageName -> PackageSource - -> Maybe Installed - -> RIO env (PackageName, Maybe AddDepRes) - upgradeFromSource name ps mInstalled = do + -> RIO env (Maybe (PackageName, Task)) + loadFromSourceTemplate name ps = do package <- loadFromSource ps if packageIsIndefinite package - then pure (name, Just $ ADRToInstall $ mkTask package ps mInstalled) - else pure (name, Nothing) + then pure $ Just (name, mkTask package ps) + else pure Nothing - upgradeFromHackage :: + loadFromHackage :: PackageName -> Installed - -> RIO env (PackageName, Maybe AddDepRes) - upgradeFromHackage name installed = do + -> RIO env (Maybe (PackageName, Task)) + loadFromHackage name installed = do let version = installedVersion installed mPkgLoc <- runRIO econfig $ getLatestHackageRevision YesRequireHackageIndex name version >>= \case @@ -190,34 +217,84 @@ upgradeFoundIndefinites loadPkg econfig combinedMap bco adrs = do Just (_rev, cfKey, treeKey) -> pure $ Just $ PLIHackage (PackageIdentifier name version) cfKey treeKey case mPkgLoc of - Nothing -> pure (name, Nothing) + Nothing -> pure Nothing Just pkgLoc -> do package <- runRIO econfig $ loadPkg pkgLoc Map.empty [] [] if packageIsIndefinite package then do - let presentMap = case installed of - Library pid ili -> installedMapGhcPkgId pid ili - _ -> Map.empty + let MissingPresentDeps missing present minMutable = + templateDeps package task = Task { configOpts = TaskConfigOpts - { missing = Set.empty + { missing , envConfig = econfig , baseConfigOpts = bco , isLocalNonExtraDep = False - , isMutable = Immutable + , isMutable = Immutable <> minMutable , pkgConfigOpts = packageConfigureOptsFromPackage package , instantiationDeps = [] } , buildHaddocks = False , allInOne = True - , present = presentMap + , present , taskType = TTRemotePackage Immutable package pkgLoc , cachePkgSrc = CacheSrcUpstream , buildTypeConfig = packageBuildTypeConfig package , backpackInstEntries = [] } - pure (name, Just $ ADRToInstall task) - else pure (name, Nothing) + pure $ Just (name, task) + else pure Nothing + + foundDependencyTargets :: Package -> [PackageName] + foundDependencyTargets package = + [ depName + | depName <- mixinTargetsFor package + , Just ADRFound{} <- [Map.lookup depName adrMap] + ] + + mixinTargetsFor :: Package -> [PackageName] + mixinTargetsFor package = + [ mixinPackageName mixin + | lib <- maybeToList package.library ++ toList package.subLibraries + , mixin <- lib.buildInfo.mixins + ] + + templateDeps :: + Package + -> MissingPresentDeps + templateDeps package = + fromRight mempty $ + runIdentity $ + processPackageDepsEither package $ \depName _depValue -> + pure (Right (case Map.lookup depName adrMap of + Just adr -> + processAdr adr + Nothing -> + dependencyFromCombinedMap depName) :: Either () MissingPresentDeps) + + dependencyFromCombinedMap :: PackageName -> MissingPresentDeps + dependencyFromCombinedMap depName = + case Map.lookup depName combinedMap of + Just (PIOnlyInstalled loc installed) -> + dependencyFromInstalled loc installed + Just (PIBoth _ installed) -> + dependencyFromInstalled Local installed + Just (PIOnlySource ps) -> + MissingPresentDeps + { missingPackages = Set.singleton $ + PackageIdentifier depName (psVersion ps) + , presentPackages = mempty + , isMutable = installLocationIsMutable $ psLocation ps + } + Nothing -> + mempty + + dependencyFromInstalled :: + InstallLocation + -> Installed + -> MissingPresentDeps + dependencyFromInstalled loc installed = + processAdr $ ADRFound loc installed -- | Post-pass: scan consumer tasks for Backpack mixins referencing indefinite -- packages and create CInst instantiation tasks. Returns the augmented list @@ -227,11 +304,13 @@ addInstantiationTasks :: -- ^ Installed package modules (from ghc-pkg dump). Used for module -- resolution when the implementing package is ADRFound (already -- installed, no Task/Package metadata available). + -> Map PackageName Task + -- ^ Source-backed templates for clean ADRFound indefinite packages. -> [(PackageName, AddDepRes)] -- ^ Original per-package ADRs -> [(ComponentKey, AddDepRes)] -- ^ Expanded component-keyed ADRs -> ([(ComponentKey, AddDepRes)], [StyleDoc]) -- ^ (Augmented with CInst tasks, warnings) -addInstantiationTasks installedModules origAdrs expandedAdrs = +addInstantiationTasks installedModules foundIndefiniteTasks origAdrs expandedAdrs = let adrMap = Map.fromList origAdrs -- Process each entry, collecting new CInst tasks and modified consumers. (newInstTasks, modifiedEntries, warns) = @@ -281,12 +360,6 @@ addInstantiationTasks installedModules origAdrs expandedAdrs = processEntry _ entry (instAcc, entryAcc, warnAcc) = (instAcc, entry : entryAcc, warnAcc) - -- Extract the Package from a Task's TaskType. - taskPackage :: Task -> Package - taskPackage t = case t.taskType of - TTLocalMutable lp -> lp.package - TTRemotePackage _ p _ -> p - -- Process all mixins for a consumer, returning new CInst entries, their -- ComponentKeys (to add as deps on the consumer), and any warnings. processAllMixins :: @@ -307,10 +380,10 @@ addInstantiationTasks installedModules origAdrs expandedAdrs = -> ([(ComponentKey, AddDepRes)], [ComponentKey], [StyleDoc]) processMixin adrMap' consumerDeps mixin (instAcc, keyAcc, warnAcc) = let depPkgName = mixinPackageName mixin - in case Map.lookup depPkgName adrMap' of - Just (ADRToInstall sigTask) - | let sigPkg = taskPackage sigTask - , packageIsIndefinite sigPkg -> + in case lookupIndefiniteTask depPkgName adrMap' of + Just sigTask -> + let sigPkg = taskPackage sigTask + in let -- Get signatures from the sig-pkg's main library. ownSigs :: [ModuleName] ownSigs = case sigPkg.library of @@ -325,7 +398,7 @@ addInstantiationTasks installedModules origAdrs expandedAdrs = inheritedSigs = -- Deduplicate: diamond deps can yield the same sig -- from multiple paths. - L.nub $ collectInheritedSigs sigPkg adrMap' Set.empty + nubOrd $ collectInheritedSigs sigPkg adrMap' Set.empty -- Determine the module mapping from the mixin's requiresRn. renaming = includeRequiresRn (mixinIncludeRenaming mixin) -- Resolve the sig-pkg's own signatures using the consumer's @@ -384,19 +457,21 @@ addInstantiationTasks installedModules origAdrs expandedAdrs = , instCk : keyAcc , resolveWarns ++ warnAcc ) - Just (ADRFound _ _) -> - -- Sig-pkg is installed and its source could not be resolved - -- (upgradeFoundIndefinites already tried). This typically means - -- a GHC boot library or a package missing from Hackage. - let w = fillSep - [ flow "Backpack: mixin referencing" - , style Current (fromPackageName depPkgName) - , flow "is skipped because that package is installed and its" - , flow "source could not be found for instantiation." - , flow "Consider adding it as a local package in stack.yaml." - ] - in (instAcc, keyAcc, w : warnAcc) - _ -> (instAcc, keyAcc, warnAcc) + Nothing -> + case Map.lookup depPkgName adrMap' of + Just ADRFound{} -> + -- Sig-pkg is installed and its source could not be resolved + -- (loadFoundIndefiniteTasks already tried). This typically means + -- a GHC boot library or a package missing from Hackage. + let w = fillSep + [ flow "Backpack: mixin referencing" + , style Current (fromPackageName depPkgName) + , flow "is skipped because that package is installed and its" + , flow "source could not be found for instantiation." + , flow "Consider adding it as a local package in stack.yaml." + ] + in (instAcc, keyAcc, w : warnAcc) + _ -> (instAcc, keyAcc, warnAcc) -- Collect signatures inherited from a package's transitive indefinite deps. -- When pkg-A depends on indefinite pkg-B, pkg-B's signatures propagate up @@ -418,17 +493,27 @@ addInstantiationTasks installedModules origAdrs expandedAdrs = collectFromDep depName | depName `Set.member` visited = [] | otherwise = - case Map.lookup depName adrMap' of - Just (ADRToInstall depTask) - | let depPkg = taskPackage depTask - , packageIsIndefinite depPkg -> - let depSigs = case depPkg.library of - Just lib -> lib.signatures - Nothing -> [] - transitive = collectInheritedSigs depPkg adrMap' - (Set.insert depName visited) - in depSigs ++ transitive - _ -> [] + case lookupIndefiniteTask depName adrMap' of + Just depTask -> + let depPkg = taskPackage depTask + depSigs = case depPkg.library of + Just lib -> lib.signatures + Nothing -> [] + transitive = collectInheritedSigs depPkg adrMap' + (Set.insert depName visited) + in depSigs ++ transitive + Nothing -> [] + + lookupIndefiniteTask :: + PackageName + -> Map PackageName AddDepRes + -> Maybe Task + lookupIndefiniteTask depPkgName adrMap' = + case Map.lookup depPkgName adrMap' of + Just (ADRToInstall task) + | packageIsIndefinite (taskPackage task) -> Just task + Just ADRFound{} -> Map.lookup depPkgName foundIndefiniteTasks + _ -> Nothing -- Resolve all signature entries for a mixin, collecting both successful -- resolutions and warnings for signatures that could not be resolved. @@ -570,7 +655,7 @@ addInstantiationTasks installedModules origAdrs expandedAdrs = ++ ":" ++ CabalText.display implMod | (sig, implPkg, implMod) <- entries ] - input = S8.pack (L.intercalate "," sorted) + input = encodeUtf8 $ T.pack $ L.intercalate "," sorted digest = hashWith SHA256 input -- Take first 16 hex chars for a short but unique-enough suffix. hexStr = Mem.convertToBase Mem.Base16 digest :: ByteString diff --git a/src/Stack/Build/ConstructPlan.hs b/src/Stack/Build/ConstructPlan.hs index 5d38d4b31b..4cf4669213 100644 --- a/src/Stack/Build/ConstructPlan.hs +++ b/src/Stack/Build/ConstructPlan.hs @@ -34,7 +34,7 @@ import RIO.State ( State, StateT (..), execState, get, modify, modify', put ) import RIO.Writer ( WriterT (..), pass, tell ) import Stack.Build.Backpack - ( addInstantiationTasks, upgradeFoundIndefinites ) + ( addInstantiationTasks, loadFoundIndefiniteTasks ) import Stack.Build.Cache ( tryGetFlagCache ) import Stack.Build.Haddock ( shouldHaddockDeps ) import Stack.Build.Source ( loadLocalPackage ) @@ -56,7 +56,8 @@ import Stack.Types.Build.ConstructPlan ( AddDepRes (..), CombinedMap, Ctx (..), LibraryMap, M , MissingPresentDeps (..), PackageInfo (..), PackageLoader , ToolWarning(..), UnregisterState (..), W (..) - , adrHasLibrary, adrVersion, isAdrToInstall, toTask + , adrHasLibrary, adrVersion, isAdrToInstall, processAdr + , toTask ) import Stack.Types.Build.Exception ( BadDependency (..), BuildException (..) @@ -95,14 +96,14 @@ import Stack.Types.NamedComponent ) import Stack.Types.Package ( ExeName (..), LocalPackage (..), Package (..) - , PackageSource (..), installedMapGhcPkgId - , packageIdentifier, psVersion, runMemoizedWith + , PackageSource (..), packageIdentifier, psVersion + , runMemoizedWith ) import Stack.Types.Plan ( ComponentKey (..), Plan (..), Task (..) , TaskConfigOpts (..), TaskType (..), componentKeyPkgName , fromComponentKey, installLocationIsMutable, taskIsTarget - , taskLocation, taskProvides, taskTargetIsMutable + , taskLocation, taskProvides ) import Stack.Types.ProjectConfig ( isPCGlobalProject ) import Stack.Types.Runner ( HasRunner (..), globalOptsL ) @@ -226,9 +227,9 @@ constructPlan errs = errlibs ++ errfinals if null errs then do - -- Upgrade ADRFound entries to ADRToInstall for indefinite packages - -- whose source is available. This ensures addInstantiationTasks can - -- clone their Task to create CInst instantiation tasks. + -- Load source-backed templates for clean installed indefinite packages. + -- This lets addInstantiationTasks clone them for CInst tasks without + -- turning their ADRFound CLib entries into build tasks. let loadPkg w x y z = applyForceCustomBuild globalCabalVersion <$> loadPackage0 w x y z -- Build a module lookup map from installed packages (dump data). @@ -239,11 +240,15 @@ constructPlan | dp <- allDumpPkgs , isNothing dp.sublib -- Only main libraries ] - adrs' <- upgradeFoundIndefinites - loadPkg econfig ctx.combinedMap ctx.baseConfigOpts adrs - let expandedAdrs = concatMap (uncurry expandToComponentKeys) adrs' + foundIndefiniteTasks <- loadFoundIndefiniteTasks + loadPkg econfig sources ctx.combinedMap ctx.baseConfigOpts adrs + let expandedAdrs = concatMap (uncurry expandToComponentKeys) adrs (withInstantiations, bpWarnings) = - addInstantiationTasks installedModules adrs' expandedAdrs + addInstantiationTasks + installedModules + foundIndefiniteTasks + adrs + expandedAdrs tasks = Map.fromList $ mapMaybe (toMaybe . second toTask) withInstantiations @@ -416,10 +421,17 @@ mkUnregisterLocal componentTasks dirtyReason localDumpPkgs initialBuildSteps = -- directly or transitively depending on it. loop Map.empty localDumpPkgs where - -- Derive a per-package task map: pick one representative task per package. + -- Derive a per-package task map: pick one representative non-CInst task per + -- package. A CInst task registers a Backpack instantiation, but it relies on + -- the existing indefinite library registration for the same package name. + -- Treating CInst-only packages as normal rebuilds unregisters that + -- indefinite unit before the CInst configure step can use it. tasks :: Map PackageName Task tasks = Map.fromList - [ (componentKeyPkgName ck, t) | (ck, t) <- Map.toList componentTasks ] + [ (componentKeyPkgName ck, t) + | (ck, t) <- Map.toList componentTasks + , not (componentKeyIsCInst ck) + ] loop :: Map GhcPkgId (PackageIdentifier, Text) @@ -505,6 +517,10 @@ mkUnregisterLocal componentTasks dirtyReason localDumpPkgs initialBuildSteps = relevantPkgName :: PackageName relevantPkgName = maybe (pkgName ident) pkgName mParentLibId + componentKeyIsCInst :: ComponentKey -> Bool + componentKeyIsCInst (ComponentKey _ CInst{}) = True + componentKeyIsCInst _ = False + -- | Given a t'LocalPackage' and its test\/benchmark 'Package', adds a -- t'Task' for running its tests and benchmarks. Resolves the package's deps -- via 'addPackageDeps' (in the common case these have already been resolved @@ -1154,31 +1170,6 @@ adrInRange pkgId name range adr = if adrVersion adr `withinRange` range , reason <> "." ] --- | Given a result of 'addDep', yields a triple indicating: (1) if the --- dependency is to be installed, its package identifier; (2) if the dependency --- is installed and a library, its package identifier and 'GhcPkgId'; and (3) if --- the dependency is, or will be when installed, mutable or immutable. -processAdr :: - AddDepRes - -> MissingPresentDeps -processAdr adr = case adr of - ADRToInstall task -> - MissingPresentDeps - { missingPackages = Set.singleton $ taskProvides task - , presentPackages = mempty - , isMutable = taskTargetIsMutable task - } - ADRFound loc installed -> - MissingPresentDeps - { missingPackages = mempty - , presentPackages = presentPackagesV - , isMutable = installLocationIsMutable loc - } - where - presentPackagesV = case installed of - Library ident installedInfo -> installedMapGhcPkgId ident installedInfo - _ -> Map.empty - checkDirtiness :: PackageSource -> Installed diff --git a/src/Stack/Types/Build/ConstructPlan.hs b/src/Stack/Types/Build/ConstructPlan.hs index 9e7dee3d93..fa0069c6ed 100644 --- a/src/Stack/Types/Build/ConstructPlan.hs +++ b/src/Stack/Types/Build/ConstructPlan.hs @@ -20,6 +20,7 @@ module Stack.Types.Build.ConstructPlan , toTask , adrVersion , adrHasLibrary + , processAdr , isAdrToInstall , Ctx (..) , PackageLoader @@ -29,6 +30,8 @@ module Stack.Types.Build.ConstructPlan ) where import Generics.Deriving.Monoid ( mappenddefault, memptydefault ) +import qualified Data.Map.Strict as Map +import qualified Data.Set as Set import RIO.Process ( HasProcessContext (..) ) import RIO.State ( StateT ) import RIO.Writer ( WriterT (..) ) @@ -52,11 +55,14 @@ import Stack.Types.Installed import Stack.Types.IsMutable ( IsMutable ) import Stack.Types.Package ( ExeName (..), LocalPackage (..), Package (..) - , PackageSource (..) + , PackageSource (..), installedMapGhcPkgId ) import Stack.Types.ParentMap ( ParentMap ) import Stack.Types.Plan - ( ComponentKey, Task (..), TaskType (..), taskProvides ) + ( ComponentKey, Task (..), TaskType (..) + , installLocationIsMutable, taskProvides + , taskTargetIsMutable + ) import Stack.Types.Platform ( HasPlatform (..) ) import Stack.Types.Runner ( HasRunner (..) ) @@ -152,6 +158,32 @@ adrHasLibrary (ADRToInstall task) = case task.taskType of adrHasLibrary (ADRFound _ Library{}) = True adrHasLibrary (ADRFound _ Executable{}) = False +-- | Given a result of 'Stack.Build.ConstructPlan.addDep', yields a triple +-- indicating: (1) if the dependency is to be installed, its package identifier; +-- (2) if the dependency is installed and a library, its package identifier and +-- 'GhcPkgId'; and (3) if the dependency is, or will be when installed, mutable +-- or immutable. +processAdr :: + AddDepRes + -> MissingPresentDeps +processAdr adr = case adr of + ADRToInstall task -> + MissingPresentDeps + { missingPackages = Set.singleton $ taskProvides task + , presentPackages = mempty + , isMutable = taskTargetIsMutable task + } + ADRFound loc installed -> + MissingPresentDeps + { missingPackages = mempty + , presentPackages = presentPackagesV + , isMutable = installLocationIsMutable loc + } + where + presentPackagesV = case installed of + Library ident installedInfo -> installedMapGhcPkgId ident installedInfo + _ -> Map.empty + data MissingPresentDeps = MissingPresentDeps { missingPackages :: !(Set PackageIdentifier) , presentPackages :: !(Map MungedPackageId GhcPkgId) diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/Main.hs b/tests/integration/tests/backpack-stale-cinst-consumer-change/Main.hs new file mode 100644 index 0000000000..e7d0e89554 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/Main.hs @@ -0,0 +1,63 @@ +-- Regression test for stale Backpack instantiation state when the final +-- Backpack consumer changes. consumer-pkg is dirty, but both indefinite +-- signature packages are clean and already installed. + +import Control.Monad ( unless ) +import Data.List ( isInfixOf ) +import System.Directory ( removeFile ) +import StackTest + +main :: IO () +main = do + -- Build all four packages. This exercises: + -- 1. str-sig CLib (indefinite, typecheck-only) + -- 2. impl-pkg CLib (concrete Str + Logger) + -- 3. str-sig CInst (instantiation with impl-pkg's Str) + -- 4. logger-sig CLib (indefinite, typecheck-only, inherits Str hole) + -- 5. logger-sig CInst (fills BOTH Logger and Str holes) + -- 6. consumer-pkg CLib + CExe + stack ["build"] + + -- Verify the consumer executable calls through the transitive chain + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain" `isInfixOf` out) $ + error $ "Expected '[LOG] Hello from transitive chain' in output, got: " + ++ show out + + replaceFile "consumer-pkg/src/Consumer.hs" $ unlines + [ "module Consumer where" + , "" + , "import LogHelper (greetWithLog)" + , "" + , "hello :: String" + , "hello = greetWithLog ++ \" after consumer edit\"" + ] + + -- Rebuild should succeed because only the final consumer changed. + stackCheckStderr ["build"] $ \err -> + expectBuildLine "consumer-pkg" "Compiling Consumer" err + + -- Verify output still correct after rebuild + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain after consumer edit" `isInfixOf` out) $ + error $ + "Expected edited consumer-pkg output after rebuild, got: " + ++ show out + +replaceFile :: FilePath -> String -> IO () +replaceFile file contents = do + removeFile file + writeFile file contents + +expectBuildLine :: String -> String -> String -> IO () +expectBuildLine package marker err = + unless (any matches $ lines err) $ + error $ + "Expected build output line containing " + ++ show package + ++ " and " + ++ show marker + ++ ", got stderr: " + ++ show err + where + matches line = package `isInfixOf` line && marker `isInfixOf` line diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/.gitignore b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/.gitignore new file mode 100644 index 0000000000..1c91379fd4 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/.gitignore @@ -0,0 +1,4 @@ +consumer-pkg.cabal +impl-pkg.cabal +logger-sig.cabal +str-sig.cabal diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/consumer-pkg/app/Main.hs b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/consumer-pkg/app/Main.hs new file mode 100644 index 0000000000..4c26592390 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/consumer-pkg/app/Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Consumer (hello) + +main :: IO () +main = putStrLn hello diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/consumer-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/consumer-pkg/package.yaml new file mode 100644 index 0000000000..ba65db2645 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/consumer-pkg/package.yaml @@ -0,0 +1,24 @@ +spec-version: 0.36.0 + +name: consumer-pkg + +dependencies: +- base + +library: + source-dirs: src + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) + - name: logger-sig + mixin: + - requires (Logger as Logger) + - impl-pkg + +executables: + consumer-demo: + source-dirs: app + main: Main.hs + dependencies: + - consumer-pkg diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/consumer-pkg/src/Consumer.hs b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/consumer-pkg/src/Consumer.hs new file mode 100644 index 0000000000..e46c8658a6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/consumer-pkg/src/Consumer.hs @@ -0,0 +1,6 @@ +module Consumer where + +import LogHelper (greetWithLog) + +hello :: String +hello = greetWithLog diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/impl-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/impl-pkg/package.yaml new file mode 100644 index 0000000000..bd4497b3d7 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/impl-pkg/package.yaml @@ -0,0 +1,9 @@ +spec-version: 0.36.0 + +name: impl-pkg + +dependencies: +- base + +library: + source-dirs: src diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/impl-pkg/src/Logger.hs b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/impl-pkg/src/Logger.hs new file mode 100644 index 0000000000..ddc7981a34 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/impl-pkg/src/Logger.hs @@ -0,0 +1,4 @@ +module Logger where + +logMessage :: String -> String +logMessage msg = "[LOG] " ++ msg diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/impl-pkg/src/Str.hs b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/impl-pkg/src/Str.hs new file mode 100644 index 0000000000..3b6d306620 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/impl-pkg/src/Str.hs @@ -0,0 +1,4 @@ +module Str where + +greeting :: String +greeting = "Hello from transitive chain" diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/logger-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/logger-sig/package.yaml new file mode 100644 index 0000000000..b9db28324f --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/logger-sig/package.yaml @@ -0,0 +1,14 @@ +spec-version: 0.36.0 + +name: logger-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: Logger + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/logger-sig/src/LogHelper.hs b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/logger-sig/src/LogHelper.hs new file mode 100644 index 0000000000..2eecba92d6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/logger-sig/src/LogHelper.hs @@ -0,0 +1,7 @@ +module LogHelper where + +import Str (greeting) +import Logger (logMessage) + +greetWithLog :: String +greetWithLog = logMessage greeting diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/logger-sig/src/Logger.hsig b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/logger-sig/src/Logger.hsig new file mode 100644 index 0000000000..446efe6fe6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/logger-sig/src/Logger.hsig @@ -0,0 +1,3 @@ +signature Logger where + +logMessage :: String -> String diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/stack.yaml b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/stack.yaml new file mode 100644 index 0000000000..f74ff881c8 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/stack.yaml @@ -0,0 +1,7 @@ +snapshot: ghc-9.10.3 + +packages: +- consumer-pkg +- impl-pkg +- logger-sig +- str-sig diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/str-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/str-sig/package.yaml new file mode 100644 index 0000000000..a6b1796ff1 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/str-sig/package.yaml @@ -0,0 +1,11 @@ +spec-version: 0.36.0 + +name: str-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: + - Str diff --git a/tests/integration/tests/backpack-stale-cinst-consumer-change/files/str-sig/src/Str.hsig b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/str-sig/src/Str.hsig new file mode 100644 index 0000000000..edb56a4a7d --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-consumer-change/files/str-sig/src/Str.hsig @@ -0,0 +1,3 @@ +signature Str where + +greeting :: String diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/Main.hs b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/Main.hs new file mode 100644 index 0000000000..fcbf648a77 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/Main.hs @@ -0,0 +1,72 @@ +-- Regression test for stale Backpack instantiation state when the deepest +-- indefinite signature package is dirty. str-sig must rebuild its indefinite +-- CLib, and the str-sig CInst must use that rebuilt unit. + +import Control.Monad ( unless ) +import Data.List ( isInfixOf ) +import System.Directory ( removeFile ) +import StackTest + +main :: IO () +main = do + -- Build all four packages. This exercises: + -- 1. str-sig CLib (indefinite, typecheck-only) + -- 2. impl-pkg CLib (concrete Str + Logger) + -- 3. str-sig CInst (instantiation with impl-pkg's Str) + -- 4. logger-sig CLib (indefinite, typecheck-only, inherits Str hole) + -- 5. logger-sig CInst (fills BOTH Logger and Str holes) + -- 6. consumer-pkg CLib + CExe + stack ["build"] + + -- Verify the consumer executable calls through the transitive chain + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain" `isInfixOf` out) $ + error $ "Expected '[LOG] Hello from transitive chain' in output, got: " + ++ show out + + replaceFile "str-sig/package.yaml" $ unlines + [ "spec-version: 0.36.0" + , "" + , "name: str-sig" + , "" + , "dependencies:" + , "- base" + , "" + , "library:" + , " source-dirs: src" + , " ghc-options:" + , " - -Wall" + , " signatures:" + , " - Str" + ] + + -- Rebuild should succeed because the CInst waits for the rebuilt indefinite + -- str-sig CLib instead of trying to use the stale installed one. + stackCheckStderr ["build"] $ \err -> do + expectBuildLine "str-sig" "Compiling Str[sig]" err + expectBuildLine "str-sig" "Str = impl-pkg" err + + -- Verify output still correct after rebuilding str-sig and its CInst. + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain" `isInfixOf` out) $ + error $ + "Expected original output after rebuilding str-sig, got: " + ++ show out + +replaceFile :: FilePath -> String -> IO () +replaceFile file contents = do + removeFile file + writeFile file contents + +expectBuildLine :: String -> String -> String -> IO () +expectBuildLine package marker err = + unless (any matches $ lines err) $ + error $ + "Expected build output line containing " + ++ show package + ++ " and " + ++ show marker + ++ ", got stderr: " + ++ show err + where + matches line = package `isInfixOf` line && marker `isInfixOf` line diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/.gitignore b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/.gitignore new file mode 100644 index 0000000000..1c91379fd4 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/.gitignore @@ -0,0 +1,4 @@ +consumer-pkg.cabal +impl-pkg.cabal +logger-sig.cabal +str-sig.cabal diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/consumer-pkg/app/Main.hs b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/consumer-pkg/app/Main.hs new file mode 100644 index 0000000000..4c26592390 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/consumer-pkg/app/Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Consumer (hello) + +main :: IO () +main = putStrLn hello diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/consumer-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/consumer-pkg/package.yaml new file mode 100644 index 0000000000..ba65db2645 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/consumer-pkg/package.yaml @@ -0,0 +1,24 @@ +spec-version: 0.36.0 + +name: consumer-pkg + +dependencies: +- base + +library: + source-dirs: src + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) + - name: logger-sig + mixin: + - requires (Logger as Logger) + - impl-pkg + +executables: + consumer-demo: + source-dirs: app + main: Main.hs + dependencies: + - consumer-pkg diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/consumer-pkg/src/Consumer.hs b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/consumer-pkg/src/Consumer.hs new file mode 100644 index 0000000000..e46c8658a6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/consumer-pkg/src/Consumer.hs @@ -0,0 +1,6 @@ +module Consumer where + +import LogHelper (greetWithLog) + +hello :: String +hello = greetWithLog diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/impl-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/impl-pkg/package.yaml new file mode 100644 index 0000000000..bd4497b3d7 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/impl-pkg/package.yaml @@ -0,0 +1,9 @@ +spec-version: 0.36.0 + +name: impl-pkg + +dependencies: +- base + +library: + source-dirs: src diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/impl-pkg/src/Logger.hs b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/impl-pkg/src/Logger.hs new file mode 100644 index 0000000000..ddc7981a34 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/impl-pkg/src/Logger.hs @@ -0,0 +1,4 @@ +module Logger where + +logMessage :: String -> String +logMessage msg = "[LOG] " ++ msg diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/impl-pkg/src/Str.hs b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/impl-pkg/src/Str.hs new file mode 100644 index 0000000000..3b6d306620 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/impl-pkg/src/Str.hs @@ -0,0 +1,4 @@ +module Str where + +greeting :: String +greeting = "Hello from transitive chain" diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/logger-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/logger-sig/package.yaml new file mode 100644 index 0000000000..b9db28324f --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/logger-sig/package.yaml @@ -0,0 +1,14 @@ +spec-version: 0.36.0 + +name: logger-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: Logger + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/logger-sig/src/LogHelper.hs b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/logger-sig/src/LogHelper.hs new file mode 100644 index 0000000000..2eecba92d6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/logger-sig/src/LogHelper.hs @@ -0,0 +1,7 @@ +module LogHelper where + +import Str (greeting) +import Logger (logMessage) + +greetWithLog :: String +greetWithLog = logMessage greeting diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/logger-sig/src/Logger.hsig b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/logger-sig/src/Logger.hsig new file mode 100644 index 0000000000..446efe6fe6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/logger-sig/src/Logger.hsig @@ -0,0 +1,3 @@ +signature Logger where + +logMessage :: String -> String diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/stack.yaml b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/stack.yaml new file mode 100644 index 0000000000..f74ff881c8 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/stack.yaml @@ -0,0 +1,7 @@ +snapshot: ghc-9.10.3 + +packages: +- consumer-pkg +- impl-pkg +- logger-sig +- str-sig diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/str-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/str-sig/package.yaml new file mode 100644 index 0000000000..a6b1796ff1 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/str-sig/package.yaml @@ -0,0 +1,11 @@ +spec-version: 0.36.0 + +name: str-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: + - Str diff --git a/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/str-sig/src/Str.hsig b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/str-sig/src/Str.hsig new file mode 100644 index 0000000000..edb56a4a7d --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-dirty-str-sig/files/str-sig/src/Str.hsig @@ -0,0 +1,3 @@ +signature Str where + +greeting :: String diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/Main.hs b/tests/integration/tests/backpack-stale-cinst-hsig-change/Main.hs new file mode 100644 index 0000000000..ca4258469c --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/Main.hs @@ -0,0 +1,72 @@ +-- Regression test for stale Backpack instantiation state when an intermediate +-- indefinite package's signature changes. The source touch keeps this focused +-- on stale CInst planning even before .hsig file tracking is fixed: +-- logger-sig is dirty, but its indefinite dependency str-sig is still clean +-- and already installed. + +import Control.Monad ( unless ) +import Data.List ( isInfixOf ) +import System.Directory ( removeFile ) +import StackTest + +main :: IO () +main = do + -- Build all four packages. This exercises: + -- 1. str-sig CLib (indefinite, typecheck-only) + -- 2. impl-pkg CLib (concrete Str + Logger) + -- 3. str-sig CInst (instantiation with impl-pkg's Str) + -- 4. logger-sig CLib (indefinite, typecheck-only, inherits Str hole) + -- 5. logger-sig CInst (fills BOTH Logger and Str holes) + -- 6. consumer-pkg CLib + CExe + stack ["build"] + + -- Verify the consumer executable calls through the transitive chain + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain" `isInfixOf` out) $ + error $ "Expected '[LOG] Hello from transitive chain' in output, got: " + ++ show out + + replaceFile "logger-sig/src/Logger.hsig" $ unlines + [ "signature Logger where" + , "" + , "logMessage :: String -> String" + , "loggerName :: String" + ] + replaceFile "logger-sig/src/LogHelper.hs" $ unlines + [ "module LogHelper where" + , "" + , "import Str (greeting)" + , "import Logger (logMessage)" + , "" + , "-- Force logger-sig dirty independently of .hsig tracking." + , "greetWithLog :: String" + , "greetWithLog = logMessage greeting" + ] + + -- Rebuild should succeed because impl-pkg already exports loggerName. + stackCheckStderr ["build"] $ \err -> + expectBuildLine "logger-sig" "Compiling Logger[sig]" err + + -- Verify output still correct after rebuild + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain" `isInfixOf` out) $ + error $ "Expected '[LOG] Hello from transitive chain' after rebuild, got: " + ++ show out + +replaceFile :: FilePath -> String -> IO () +replaceFile file contents = do + removeFile file + writeFile file contents + +expectBuildLine :: String -> String -> String -> IO () +expectBuildLine package marker err = + unless (any matches $ lines err) $ + error $ + "Expected build output line containing " + ++ show package + ++ " and " + ++ show marker + ++ ", got stderr: " + ++ show err + where + matches line = package `isInfixOf` line && marker `isInfixOf` line diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/.gitignore b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/.gitignore new file mode 100644 index 0000000000..1c91379fd4 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/.gitignore @@ -0,0 +1,4 @@ +consumer-pkg.cabal +impl-pkg.cabal +logger-sig.cabal +str-sig.cabal diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/consumer-pkg/app/Main.hs b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/consumer-pkg/app/Main.hs new file mode 100644 index 0000000000..4c26592390 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/consumer-pkg/app/Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Consumer (hello) + +main :: IO () +main = putStrLn hello diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/consumer-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/consumer-pkg/package.yaml new file mode 100644 index 0000000000..ba65db2645 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/consumer-pkg/package.yaml @@ -0,0 +1,24 @@ +spec-version: 0.36.0 + +name: consumer-pkg + +dependencies: +- base + +library: + source-dirs: src + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) + - name: logger-sig + mixin: + - requires (Logger as Logger) + - impl-pkg + +executables: + consumer-demo: + source-dirs: app + main: Main.hs + dependencies: + - consumer-pkg diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/consumer-pkg/src/Consumer.hs b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/consumer-pkg/src/Consumer.hs new file mode 100644 index 0000000000..e46c8658a6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/consumer-pkg/src/Consumer.hs @@ -0,0 +1,6 @@ +module Consumer where + +import LogHelper (greetWithLog) + +hello :: String +hello = greetWithLog diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/impl-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/impl-pkg/package.yaml new file mode 100644 index 0000000000..bd4497b3d7 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/impl-pkg/package.yaml @@ -0,0 +1,9 @@ +spec-version: 0.36.0 + +name: impl-pkg + +dependencies: +- base + +library: + source-dirs: src diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/impl-pkg/src/Logger.hs b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/impl-pkg/src/Logger.hs new file mode 100644 index 0000000000..62c1318517 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/impl-pkg/src/Logger.hs @@ -0,0 +1,7 @@ +module Logger where + +logMessage :: String -> String +logMessage msg = "[LOG] " ++ msg + +loggerName :: String +loggerName = "transitive logger" diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/impl-pkg/src/Str.hs b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/impl-pkg/src/Str.hs new file mode 100644 index 0000000000..3b6d306620 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/impl-pkg/src/Str.hs @@ -0,0 +1,4 @@ +module Str where + +greeting :: String +greeting = "Hello from transitive chain" diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/logger-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/logger-sig/package.yaml new file mode 100644 index 0000000000..b9db28324f --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/logger-sig/package.yaml @@ -0,0 +1,14 @@ +spec-version: 0.36.0 + +name: logger-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: Logger + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/logger-sig/src/LogHelper.hs b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/logger-sig/src/LogHelper.hs new file mode 100644 index 0000000000..2eecba92d6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/logger-sig/src/LogHelper.hs @@ -0,0 +1,7 @@ +module LogHelper where + +import Str (greeting) +import Logger (logMessage) + +greetWithLog :: String +greetWithLog = logMessage greeting diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/logger-sig/src/Logger.hsig b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/logger-sig/src/Logger.hsig new file mode 100644 index 0000000000..446efe6fe6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/logger-sig/src/Logger.hsig @@ -0,0 +1,3 @@ +signature Logger where + +logMessage :: String -> String diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/stack.yaml b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/stack.yaml new file mode 100644 index 0000000000..f74ff881c8 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/stack.yaml @@ -0,0 +1,7 @@ +snapshot: ghc-9.10.3 + +packages: +- consumer-pkg +- impl-pkg +- logger-sig +- str-sig diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/str-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/str-sig/package.yaml new file mode 100644 index 0000000000..a6b1796ff1 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/str-sig/package.yaml @@ -0,0 +1,11 @@ +spec-version: 0.36.0 + +name: str-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: + - Str diff --git a/tests/integration/tests/backpack-stale-cinst-hsig-change/files/str-sig/src/Str.hsig b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/str-sig/src/Str.hsig new file mode 100644 index 0000000000..edb56a4a7d --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-hsig-change/files/str-sig/src/Str.hsig @@ -0,0 +1,3 @@ +signature Str where + +greeting :: String diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/Main.hs b/tests/integration/tests/backpack-stale-cinst-impl-change/Main.hs new file mode 100644 index 0000000000..634f84606f --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/Main.hs @@ -0,0 +1,61 @@ +-- Regression test for stale Backpack instantiation state when the concrete +-- implementation package changes. The signature packages are clean, but their +-- concrete instantiations depend on impl-pkg's modules. + +import Control.Monad ( unless ) +import Data.List ( isInfixOf ) +import System.Directory ( removeFile ) +import StackTest + +main :: IO () +main = do + -- Build all four packages. This exercises: + -- 1. str-sig CLib (indefinite, typecheck-only) + -- 2. impl-pkg CLib (concrete Str + Logger) + -- 3. str-sig CInst (instantiation with impl-pkg's Str) + -- 4. logger-sig CLib (indefinite, typecheck-only, inherits Str hole) + -- 5. logger-sig CInst (fills BOTH Logger and Str holes) + -- 6. consumer-pkg CLib + CExe + stack ["build"] + + -- Verify the consumer executable calls through the transitive chain + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain" `isInfixOf` out) $ + error $ "Expected '[LOG] Hello from transitive chain' in output, got: " + ++ show out + + replaceFile "impl-pkg/src/Str.hs" $ unlines + [ "module Str where" + , "" + , "greeting :: String" + , "greeting = \"Hello from changed implementation\"" + ] + + -- Rebuild should succeed because only the concrete implementation changed. + stackCheckStderr ["build"] $ \err -> + expectBuildLine "impl-pkg" "Compiling Str" err + + -- Verify output reflects the changed implementation after rebuild + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from changed implementation" `isInfixOf` out) $ + error $ + "Expected changed impl-pkg output after rebuild, got: " + ++ show out + +replaceFile :: FilePath -> String -> IO () +replaceFile file contents = do + removeFile file + writeFile file contents + +expectBuildLine :: String -> String -> String -> IO () +expectBuildLine package marker err = + unless (any matches $ lines err) $ + error $ + "Expected build output line containing " + ++ show package + ++ " and " + ++ show marker + ++ ", got stderr: " + ++ show err + where + matches line = package `isInfixOf` line && marker `isInfixOf` line diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/.gitignore b/tests/integration/tests/backpack-stale-cinst-impl-change/files/.gitignore new file mode 100644 index 0000000000..1c91379fd4 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/.gitignore @@ -0,0 +1,4 @@ +consumer-pkg.cabal +impl-pkg.cabal +logger-sig.cabal +str-sig.cabal diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/consumer-pkg/app/Main.hs b/tests/integration/tests/backpack-stale-cinst-impl-change/files/consumer-pkg/app/Main.hs new file mode 100644 index 0000000000..4c26592390 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/consumer-pkg/app/Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Consumer (hello) + +main :: IO () +main = putStrLn hello diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/consumer-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-impl-change/files/consumer-pkg/package.yaml new file mode 100644 index 0000000000..ba65db2645 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/consumer-pkg/package.yaml @@ -0,0 +1,24 @@ +spec-version: 0.36.0 + +name: consumer-pkg + +dependencies: +- base + +library: + source-dirs: src + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) + - name: logger-sig + mixin: + - requires (Logger as Logger) + - impl-pkg + +executables: + consumer-demo: + source-dirs: app + main: Main.hs + dependencies: + - consumer-pkg diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/consumer-pkg/src/Consumer.hs b/tests/integration/tests/backpack-stale-cinst-impl-change/files/consumer-pkg/src/Consumer.hs new file mode 100644 index 0000000000..e46c8658a6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/consumer-pkg/src/Consumer.hs @@ -0,0 +1,6 @@ +module Consumer where + +import LogHelper (greetWithLog) + +hello :: String +hello = greetWithLog diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/impl-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-impl-change/files/impl-pkg/package.yaml new file mode 100644 index 0000000000..bd4497b3d7 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/impl-pkg/package.yaml @@ -0,0 +1,9 @@ +spec-version: 0.36.0 + +name: impl-pkg + +dependencies: +- base + +library: + source-dirs: src diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/impl-pkg/src/Logger.hs b/tests/integration/tests/backpack-stale-cinst-impl-change/files/impl-pkg/src/Logger.hs new file mode 100644 index 0000000000..ddc7981a34 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/impl-pkg/src/Logger.hs @@ -0,0 +1,4 @@ +module Logger where + +logMessage :: String -> String +logMessage msg = "[LOG] " ++ msg diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/impl-pkg/src/Str.hs b/tests/integration/tests/backpack-stale-cinst-impl-change/files/impl-pkg/src/Str.hs new file mode 100644 index 0000000000..3b6d306620 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/impl-pkg/src/Str.hs @@ -0,0 +1,4 @@ +module Str where + +greeting :: String +greeting = "Hello from transitive chain" diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/logger-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-impl-change/files/logger-sig/package.yaml new file mode 100644 index 0000000000..b9db28324f --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/logger-sig/package.yaml @@ -0,0 +1,14 @@ +spec-version: 0.36.0 + +name: logger-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: Logger + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/logger-sig/src/LogHelper.hs b/tests/integration/tests/backpack-stale-cinst-impl-change/files/logger-sig/src/LogHelper.hs new file mode 100644 index 0000000000..2eecba92d6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/logger-sig/src/LogHelper.hs @@ -0,0 +1,7 @@ +module LogHelper where + +import Str (greeting) +import Logger (logMessage) + +greetWithLog :: String +greetWithLog = logMessage greeting diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/logger-sig/src/Logger.hsig b/tests/integration/tests/backpack-stale-cinst-impl-change/files/logger-sig/src/Logger.hsig new file mode 100644 index 0000000000..446efe6fe6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/logger-sig/src/Logger.hsig @@ -0,0 +1,3 @@ +signature Logger where + +logMessage :: String -> String diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/stack.yaml b/tests/integration/tests/backpack-stale-cinst-impl-change/files/stack.yaml new file mode 100644 index 0000000000..f74ff881c8 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/stack.yaml @@ -0,0 +1,7 @@ +snapshot: ghc-9.10.3 + +packages: +- consumer-pkg +- impl-pkg +- logger-sig +- str-sig diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/str-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-impl-change/files/str-sig/package.yaml new file mode 100644 index 0000000000..a6b1796ff1 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/str-sig/package.yaml @@ -0,0 +1,11 @@ +spec-version: 0.36.0 + +name: str-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: + - Str diff --git a/tests/integration/tests/backpack-stale-cinst-impl-change/files/str-sig/src/Str.hsig b/tests/integration/tests/backpack-stale-cinst-impl-change/files/str-sig/src/Str.hsig new file mode 100644 index 0000000000..edb56a4a7d --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-impl-change/files/str-sig/src/Str.hsig @@ -0,0 +1,3 @@ +signature Str where + +greeting :: String diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/Main.hs b/tests/integration/tests/backpack-stale-cinst-inherited-hole/Main.hs new file mode 100644 index 0000000000..366b7e21ea --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/Main.hs @@ -0,0 +1,66 @@ +-- Regression test for stale Backpack instantiation state when the final +-- Backpack consumer changes and one required hole is inherited. consumer-pkg +-- mixes in only logger-sig; logger-sig depends on str-sig, so Stack must load +-- both clean installed signature packages to instantiate logger-sig with +-- Logger and Str. + +import Control.Monad ( unless ) +import Data.List ( isInfixOf ) +import System.Directory ( removeFile ) +import StackTest + +main :: IO () +main = do + -- Build all four packages. This exercises: + -- 1. str-sig CLib (indefinite, typecheck-only) + -- 2. impl-pkg CLib (concrete Str + Logger) + -- 3. logger-sig CLib (indefinite, typecheck-only, inherits Str hole) + -- 4. logger-sig CInst (fills BOTH Logger and Str holes) + -- 5. consumer-pkg CLib + CExe + stack ["build"] + + -- Verify the consumer executable calls through the transitive chain + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain" `isInfixOf` out) $ + error $ "Expected '[LOG] Hello from transitive chain' in output, got: " + ++ show out + + replaceFile "consumer-pkg/src/Consumer.hs" $ unlines + [ "module Consumer where" + , "" + , "import LogHelper (greetWithLog)" + , "" + , "hello :: String" + , "hello = greetWithLog ++ \" after consumer edit\"" + ] + + -- Rebuild should succeed because Stack loads logger-sig and its transitive + -- indefinite dependency str-sig as templates for the CInst task. + stackCheckStderr ["build"] $ \err -> do + expectBuildLine "logger-sig" "Str = impl-pkg" err + expectBuildLine "consumer-pkg" "Compiling Consumer" err + + -- Verify output still correct after rebuild + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain after consumer edit" `isInfixOf` out) $ + error $ + "Expected edited consumer-pkg output after rebuild, got: " + ++ show out + +replaceFile :: FilePath -> String -> IO () +replaceFile file contents = do + removeFile file + writeFile file contents + +expectBuildLine :: String -> String -> String -> IO () +expectBuildLine package marker err = + unless (any matches $ lines err) $ + error $ + "Expected build output line containing " + ++ show package + ++ " and " + ++ show marker + ++ ", got stderr: " + ++ show err + where + matches line = package `isInfixOf` line && marker `isInfixOf` line diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/.gitignore b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/.gitignore new file mode 100644 index 0000000000..1c91379fd4 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/.gitignore @@ -0,0 +1,4 @@ +consumer-pkg.cabal +impl-pkg.cabal +logger-sig.cabal +str-sig.cabal diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/consumer-pkg/app/Main.hs b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/consumer-pkg/app/Main.hs new file mode 100644 index 0000000000..4c26592390 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/consumer-pkg/app/Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Consumer (hello) + +main :: IO () +main = putStrLn hello diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/consumer-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/consumer-pkg/package.yaml new file mode 100644 index 0000000000..c556c7e413 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/consumer-pkg/package.yaml @@ -0,0 +1,21 @@ +spec-version: 0.36.0 + +name: consumer-pkg + +dependencies: +- base + +library: + source-dirs: src + dependencies: + - name: logger-sig + mixin: + - requires (Logger as Logger) + - impl-pkg + +executables: + consumer-demo: + source-dirs: app + main: Main.hs + dependencies: + - consumer-pkg diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/consumer-pkg/src/Consumer.hs b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/consumer-pkg/src/Consumer.hs new file mode 100644 index 0000000000..e46c8658a6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/consumer-pkg/src/Consumer.hs @@ -0,0 +1,6 @@ +module Consumer where + +import LogHelper (greetWithLog) + +hello :: String +hello = greetWithLog diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/impl-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/impl-pkg/package.yaml new file mode 100644 index 0000000000..bd4497b3d7 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/impl-pkg/package.yaml @@ -0,0 +1,9 @@ +spec-version: 0.36.0 + +name: impl-pkg + +dependencies: +- base + +library: + source-dirs: src diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/impl-pkg/src/Logger.hs b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/impl-pkg/src/Logger.hs new file mode 100644 index 0000000000..ddc7981a34 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/impl-pkg/src/Logger.hs @@ -0,0 +1,4 @@ +module Logger where + +logMessage :: String -> String +logMessage msg = "[LOG] " ++ msg diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/impl-pkg/src/Str.hs b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/impl-pkg/src/Str.hs new file mode 100644 index 0000000000..3b6d306620 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/impl-pkg/src/Str.hs @@ -0,0 +1,4 @@ +module Str where + +greeting :: String +greeting = "Hello from transitive chain" diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/logger-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/logger-sig/package.yaml new file mode 100644 index 0000000000..b9db28324f --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/logger-sig/package.yaml @@ -0,0 +1,14 @@ +spec-version: 0.36.0 + +name: logger-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: Logger + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/logger-sig/src/LogHelper.hs b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/logger-sig/src/LogHelper.hs new file mode 100644 index 0000000000..2eecba92d6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/logger-sig/src/LogHelper.hs @@ -0,0 +1,7 @@ +module LogHelper where + +import Str (greeting) +import Logger (logMessage) + +greetWithLog :: String +greetWithLog = logMessage greeting diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/logger-sig/src/Logger.hsig b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/logger-sig/src/Logger.hsig new file mode 100644 index 0000000000..446efe6fe6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/logger-sig/src/Logger.hsig @@ -0,0 +1,3 @@ +signature Logger where + +logMessage :: String -> String diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/stack.yaml b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/stack.yaml new file mode 100644 index 0000000000..f74ff881c8 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/stack.yaml @@ -0,0 +1,7 @@ +snapshot: ghc-9.10.3 + +packages: +- consumer-pkg +- impl-pkg +- logger-sig +- str-sig diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/str-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/str-sig/package.yaml new file mode 100644 index 0000000000..a6b1796ff1 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/str-sig/package.yaml @@ -0,0 +1,11 @@ +spec-version: 0.36.0 + +name: str-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: + - Str diff --git a/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/str-sig/src/Str.hsig b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/str-sig/src/Str.hsig new file mode 100644 index 0000000000..edb56a4a7d --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-inherited-hole/files/str-sig/src/Str.hsig @@ -0,0 +1,3 @@ +signature Str where + +greeting :: String diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/Main.hs b/tests/integration/tests/backpack-stale-cinst-sig-source-change/Main.hs new file mode 100644 index 0000000000..d678f167f4 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/Main.hs @@ -0,0 +1,64 @@ +-- Regression test for stale Backpack instantiation state when an intermediate +-- indefinite package's normal source changes. logger-sig is dirty, but its +-- indefinite dependency str-sig is still clean and already installed. + +import Control.Monad ( unless ) +import Data.List ( isInfixOf ) +import System.Directory ( removeFile ) +import StackTest + +main :: IO () +main = do + -- Build all four packages. This exercises: + -- 1. str-sig CLib (indefinite, typecheck-only) + -- 2. impl-pkg CLib (concrete Str + Logger) + -- 3. str-sig CInst (instantiation with impl-pkg's Str) + -- 4. logger-sig CLib (indefinite, typecheck-only, inherits Str hole) + -- 5. logger-sig CInst (fills BOTH Logger and Str holes) + -- 6. consumer-pkg CLib + CExe + stack ["build"] + + -- Verify the consumer executable calls through the transitive chain + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain" `isInfixOf` out) $ + error $ "Expected '[LOG] Hello from transitive chain' in output, got: " + ++ show out + + replaceFile "logger-sig/src/LogHelper.hs" $ unlines + [ "module LogHelper where" + , "" + , "import Str (greeting)" + , "import Logger (logMessage)" + , "" + , "greetWithLog :: String" + , "greetWithLog = logMessage (greeting ++ \" after source edit\")" + ] + + -- Rebuild should succeed because only logger-sig's own source changed. + stackCheckStderr ["build"] $ \err -> + expectBuildLine "logger-sig" "Compiling LogHelper" err + + -- Verify output still correct after rebuild + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain after source edit" `isInfixOf` out) $ + error $ + "Expected edited logger-sig output after rebuild, got: " + ++ show out + +replaceFile :: FilePath -> String -> IO () +replaceFile file contents = do + removeFile file + writeFile file contents + +expectBuildLine :: String -> String -> String -> IO () +expectBuildLine package marker err = + unless (any matches $ lines err) $ + error $ + "Expected build output line containing " + ++ show package + ++ " and " + ++ show marker + ++ ", got stderr: " + ++ show err + where + matches line = package `isInfixOf` line && marker `isInfixOf` line diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/.gitignore b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/.gitignore new file mode 100644 index 0000000000..1c91379fd4 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/.gitignore @@ -0,0 +1,4 @@ +consumer-pkg.cabal +impl-pkg.cabal +logger-sig.cabal +str-sig.cabal diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/consumer-pkg/app/Main.hs b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/consumer-pkg/app/Main.hs new file mode 100644 index 0000000000..4c26592390 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/consumer-pkg/app/Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Consumer (hello) + +main :: IO () +main = putStrLn hello diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/consumer-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/consumer-pkg/package.yaml new file mode 100644 index 0000000000..ba65db2645 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/consumer-pkg/package.yaml @@ -0,0 +1,24 @@ +spec-version: 0.36.0 + +name: consumer-pkg + +dependencies: +- base + +library: + source-dirs: src + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) + - name: logger-sig + mixin: + - requires (Logger as Logger) + - impl-pkg + +executables: + consumer-demo: + source-dirs: app + main: Main.hs + dependencies: + - consumer-pkg diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/consumer-pkg/src/Consumer.hs b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/consumer-pkg/src/Consumer.hs new file mode 100644 index 0000000000..e46c8658a6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/consumer-pkg/src/Consumer.hs @@ -0,0 +1,6 @@ +module Consumer where + +import LogHelper (greetWithLog) + +hello :: String +hello = greetWithLog diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/impl-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/impl-pkg/package.yaml new file mode 100644 index 0000000000..bd4497b3d7 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/impl-pkg/package.yaml @@ -0,0 +1,9 @@ +spec-version: 0.36.0 + +name: impl-pkg + +dependencies: +- base + +library: + source-dirs: src diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/impl-pkg/src/Logger.hs b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/impl-pkg/src/Logger.hs new file mode 100644 index 0000000000..ddc7981a34 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/impl-pkg/src/Logger.hs @@ -0,0 +1,4 @@ +module Logger where + +logMessage :: String -> String +logMessage msg = "[LOG] " ++ msg diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/impl-pkg/src/Str.hs b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/impl-pkg/src/Str.hs new file mode 100644 index 0000000000..3b6d306620 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/impl-pkg/src/Str.hs @@ -0,0 +1,4 @@ +module Str where + +greeting :: String +greeting = "Hello from transitive chain" diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/logger-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/logger-sig/package.yaml new file mode 100644 index 0000000000..b9db28324f --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/logger-sig/package.yaml @@ -0,0 +1,14 @@ +spec-version: 0.36.0 + +name: logger-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: Logger + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/logger-sig/src/LogHelper.hs b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/logger-sig/src/LogHelper.hs new file mode 100644 index 0000000000..2eecba92d6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/logger-sig/src/LogHelper.hs @@ -0,0 +1,7 @@ +module LogHelper where + +import Str (greeting) +import Logger (logMessage) + +greetWithLog :: String +greetWithLog = logMessage greeting diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/logger-sig/src/Logger.hsig b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/logger-sig/src/Logger.hsig new file mode 100644 index 0000000000..446efe6fe6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/logger-sig/src/Logger.hsig @@ -0,0 +1,3 @@ +signature Logger where + +logMessage :: String -> String diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/stack.yaml b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/stack.yaml new file mode 100644 index 0000000000..f74ff881c8 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/stack.yaml @@ -0,0 +1,7 @@ +snapshot: ghc-9.10.3 + +packages: +- consumer-pkg +- impl-pkg +- logger-sig +- str-sig diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/str-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/str-sig/package.yaml new file mode 100644 index 0000000000..a6b1796ff1 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/str-sig/package.yaml @@ -0,0 +1,11 @@ +spec-version: 0.36.0 + +name: str-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: + - Str diff --git a/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/str-sig/src/Str.hsig b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/str-sig/src/Str.hsig new file mode 100644 index 0000000000..edb56a4a7d --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-sig-source-change/files/str-sig/src/Str.hsig @@ -0,0 +1,3 @@ +signature Str where + +greeting :: String diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/Main.hs b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/Main.hs new file mode 100644 index 0000000000..71919a71ba --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/Main.hs @@ -0,0 +1,75 @@ +-- Regression test for stale Backpack instantiation state when the final +-- Backpack consumer changes and one required hole is inherited from a snapshot +-- package. consumer-pkg mixes in only logger-sig; logger-sig depends on +-- str-sig, which is installed from a local snapshot archive. Stack must use the +-- source map, not just CombinedMap, to load str-sig's signature metadata. + +import Control.Monad ( unless ) +import Data.List ( isInfixOf ) +import System.Directory ( createDirectoryIfMissing, removeFile ) +import StackTest + +main :: IO () +main = do + createDirectoryIfMissing True "snapshots" + stack + [ "--stack-yaml", "stack-bootstrap.yaml" + , "sdist", "str-sig" + , "--ignore-check" + , "--tar-dir", "snapshots" + ] + + -- Build all four packages. This exercises: + -- 1. str-sig CLib (indefinite, typecheck-only) + -- 2. impl-pkg CLib (concrete Str + Logger) + -- 3. logger-sig CLib (indefinite, typecheck-only, inherits Str hole) + -- 4. logger-sig CInst (fills BOTH Logger and Str holes) + -- 5. consumer-pkg CLib + CExe + stack ["build"] + + -- Verify the consumer executable calls through the transitive chain + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain" `isInfixOf` out) $ + error $ "Expected '[LOG] Hello from transitive chain' in output, got: " + ++ show out + + replaceFile "consumer-pkg/src/Consumer.hs" $ unlines + [ "module Consumer where" + , "" + , "import LogHelper (greetWithLog)" + , "" + , "hello :: String" + , "hello = greetWithLog ++ \" after consumer edit\"" + ] + + -- Rebuild should succeed because Stack loads logger-sig and its transitive + -- snapshot-installed indefinite dependency str-sig as templates for the + -- CInst task. + stackCheckStderr ["build"] $ \err -> do + expectBuildLine "logger-sig" "Str = impl-pkg" err + expectBuildLine "consumer-pkg" "Compiling Consumer" err + + -- Verify output still correct after rebuild + stackCheckStdout ["exec", "consumer-demo"] $ \out -> + unless ("[LOG] Hello from transitive chain after consumer edit" `isInfixOf` out) $ + error $ + "Expected edited consumer-pkg output after rebuild, got: " + ++ show out + +replaceFile :: FilePath -> String -> IO () +replaceFile file contents = do + removeFile file + writeFile file contents + +expectBuildLine :: String -> String -> String -> IO () +expectBuildLine package marker err = + unless (any matches $ lines err) $ + error $ + "Expected build output line containing " + ++ show package + ++ " and " + ++ show marker + ++ ", got stderr: " + ++ show err + where + matches line = package `isInfixOf` line && marker `isInfixOf` line diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/.gitignore b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/.gitignore new file mode 100644 index 0000000000..83939e06e4 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/.gitignore @@ -0,0 +1,7 @@ +consumer-pkg.cabal +impl-pkg.cabal +logger-sig.cabal +str-sig.cabal +snapshots/*.tar.gz +stack.yaml.lock +snapshot.yaml.lock diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/consumer-pkg/app/Main.hs b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/consumer-pkg/app/Main.hs new file mode 100644 index 0000000000..4c26592390 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/consumer-pkg/app/Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Consumer (hello) + +main :: IO () +main = putStrLn hello diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/consumer-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/consumer-pkg/package.yaml new file mode 100644 index 0000000000..c556c7e413 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/consumer-pkg/package.yaml @@ -0,0 +1,21 @@ +spec-version: 0.36.0 + +name: consumer-pkg + +dependencies: +- base + +library: + source-dirs: src + dependencies: + - name: logger-sig + mixin: + - requires (Logger as Logger) + - impl-pkg + +executables: + consumer-demo: + source-dirs: app + main: Main.hs + dependencies: + - consumer-pkg diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/consumer-pkg/src/Consumer.hs b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/consumer-pkg/src/Consumer.hs new file mode 100644 index 0000000000..e46c8658a6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/consumer-pkg/src/Consumer.hs @@ -0,0 +1,6 @@ +module Consumer where + +import LogHelper (greetWithLog) + +hello :: String +hello = greetWithLog diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/impl-pkg/package.yaml b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/impl-pkg/package.yaml new file mode 100644 index 0000000000..bd4497b3d7 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/impl-pkg/package.yaml @@ -0,0 +1,9 @@ +spec-version: 0.36.0 + +name: impl-pkg + +dependencies: +- base + +library: + source-dirs: src diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/impl-pkg/src/Logger.hs b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/impl-pkg/src/Logger.hs new file mode 100644 index 0000000000..ddc7981a34 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/impl-pkg/src/Logger.hs @@ -0,0 +1,4 @@ +module Logger where + +logMessage :: String -> String +logMessage msg = "[LOG] " ++ msg diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/impl-pkg/src/Str.hs b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/impl-pkg/src/Str.hs new file mode 100644 index 0000000000..3b6d306620 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/impl-pkg/src/Str.hs @@ -0,0 +1,4 @@ +module Str where + +greeting :: String +greeting = "Hello from transitive chain" diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/logger-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/logger-sig/package.yaml new file mode 100644 index 0000000000..b9db28324f --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/logger-sig/package.yaml @@ -0,0 +1,14 @@ +spec-version: 0.36.0 + +name: logger-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: Logger + dependencies: + - name: str-sig + mixin: + - requires (Str as Str) diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/logger-sig/src/LogHelper.hs b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/logger-sig/src/LogHelper.hs new file mode 100644 index 0000000000..2eecba92d6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/logger-sig/src/LogHelper.hs @@ -0,0 +1,7 @@ +module LogHelper where + +import Str (greeting) +import Logger (logMessage) + +greetWithLog :: String +greetWithLog = logMessage greeting diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/logger-sig/src/Logger.hsig b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/logger-sig/src/Logger.hsig new file mode 100644 index 0000000000..446efe6fe6 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/logger-sig/src/Logger.hsig @@ -0,0 +1,3 @@ +signature Logger where + +logMessage :: String -> String diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/snapshot.yaml b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/snapshot.yaml new file mode 100644 index 0000000000..54532d2e55 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/snapshot.yaml @@ -0,0 +1,6 @@ +name: backpack-stale-cinst-snapshot + +snapshot: ghc-9.10.3 + +packages: +- archive: snapshots/str-sig-0.0.0.tar.gz diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/stack-bootstrap.yaml b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/stack-bootstrap.yaml new file mode 100644 index 0000000000..7fb56effb0 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/stack-bootstrap.yaml @@ -0,0 +1,4 @@ +snapshot: ghc-9.10.3 + +packages: +- str-sig diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/stack.yaml b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/stack.yaml new file mode 100644 index 0000000000..6d3992aabe --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/stack.yaml @@ -0,0 +1,6 @@ +snapshot: snapshot.yaml + +packages: +- consumer-pkg +- impl-pkg +- logger-sig diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/str-sig/package.yaml b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/str-sig/package.yaml new file mode 100644 index 0000000000..a6b1796ff1 --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/str-sig/package.yaml @@ -0,0 +1,11 @@ +spec-version: 0.36.0 + +name: str-sig + +dependencies: +- base + +library: + source-dirs: src + signatures: + - Str diff --git a/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/str-sig/src/Str.hsig b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/str-sig/src/Str.hsig new file mode 100644 index 0000000000..edb56a4a7d --- /dev/null +++ b/tests/integration/tests/backpack-stale-cinst-snapshot-inherited-hole/files/str-sig/src/Str.hsig @@ -0,0 +1,3 @@ +signature Str where + +greeting :: String diff --git a/tests/unit/Stack/Build/ConstructPlanSpec.hs b/tests/unit/Stack/Build/ConstructPlanSpec.hs index c57d34cc90..b19513bf2d 100644 --- a/tests/unit/Stack/Build/ConstructPlanSpec.hs +++ b/tests/unit/Stack/Build/ConstructPlanSpec.hs @@ -28,7 +28,7 @@ import Distribution.Types.PackageName ( mkPackageName ) import Distribution.Types.Version ( mkVersion ) import Distribution.Types.VersionRange ( anyVersion ) import Distribution.ModuleName ( ModuleName ) -import Stack.Build.Backpack ( addInstantiationTasks ) +import qualified Stack.Build.Backpack as Backpack import Stack.Build.ConstructPlan ( shouldSplitComponents ) import Stack.Build.ExecutePackage ( findGhcPkgId, mkInstantiateWithOpts ) @@ -71,6 +71,14 @@ import Test.Hspec , shouldBe, shouldSatisfy ) +addInstantiationTasks :: + Map PackageName (Set ModuleName) + -> [(PackageName, AddDepRes)] + -> [(ComponentKey, AddDepRes)] + -> ([(ComponentKey, AddDepRes)], [StyleDoc]) +addInstantiationTasks installedModules = + Backpack.addInstantiationTasks installedModules Map.empty + main :: IO () main = hspec spec