diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index daf617389d..af96fce828 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -5,7 +5,7 @@ about: Report a bug in Stack Please follow the steps below for reporting a bug in Stack: -Make sure that you are using the latest release (currently Stack 3.9.3). See the +Make sure that you are using the latest release (currently Stack 3.11.1). See the [upgrade instructions](http://docs.haskellstack.org/en/stable/install_and_upgrade/#upgrade) to upgrade. @@ -45,7 +45,7 @@ stack --verbose ~~~text stack --version -Version 3.9.3, Git revision c7eb8487a82d5c3e0b88d56f8b8a98be23223eb5 x86_64 hpack-0.39.1 +Version 3.11.1, Git revision 979bf6339fd4d5837c4d2fa8166dc715eef30ee2 x86_64 hpack-0.39.6 ~~~ ### Method of installation diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 699675f815..81c6f24a36 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -3,7 +3,7 @@ name: Feature Request about: Request a feature be added to Stack, or discuss such a feature --- -Make sure that you are using the latest release (currently Stack 3.9.3). See the +Make sure that you are using the latest release (currently Stack 3.11.1). See the [upgrade instructions](http://docs.haskellstack.org/en/stable/install_and_upgrade/#upgrade) to upgrade. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 90e340d705..68a14bf866 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -16,7 +16,7 @@ the [stack-templates](https://github.com/commercialhaskell/stack-templates) repository instead. If you still want to ask the question here instead, please make sure that you -are using the latest release (currently Stack 3.9.3). See the +are using the latest release (currently Stack 3.11.1). See the [upgrade instructions](http://docs.haskellstack.org/en/stable/install_and_upgrade/#upgrade) to upgrade. @@ -24,7 +24,7 @@ to upgrade. ~~~text stack --version -Version 3.9.3, Git revision c7eb8487a82d5c3e0b88d56f8b8a98be23223eb5 x86_64 hpack-0.39.1 +Version 3.11.1, Git revision 979bf6339fd4d5837c4d2fa8166dc715eef30ee2 x86_64 hpack-0.39.6 ~~~ ### Method of installation diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 36880d2a9f..e289cfee80 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -76,22 +76,17 @@ jobs: ~\AppData\Roaming\stack ~\AppData\Local\Programs\stack key: ${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('stack.yaml') }}-${{ matrix.cache-bust }} - - name: Install NSIS 3.12 on Windows + # Also upgrades to a 'large strings' build of NSIS 3.12 tool. See + # https://nsis.sourceforge.io/Special_Builds. + - name: Install and upgrade NSIS 3.12 on Windows if: startsWith(runner.os, 'Windows') - uses: repolevedavaj/install-nsis@v1.2.0 + uses: repolevedavaj/install-nsis@v1.2.1 with: nsis-version: '3.12' - # Upgrades to a 'large strings' build of NSIS 3.10 tool. See - # https://nsis.sourceforge.io/Special_Builds. - - name: Upgrade NSIS 3.12 on Windows + - name: Check NSIS 3.12 on Windows if: startsWith(runner.os, 'Windows') shell: bash run: | - # wget is not available but the Stack-supplied MSYS2 will provide it - stack exec -- wget -O nsis-3.12-strlen_8192.zip https://downloads.sourceforge.net/nsis/NSIS%203/3.12/nsis-3.12-strlen_8192.zip - 7z x -aoa -o"/c/Program Files (x86)/NSIS" nsis-3.12-strlen_8192.zip - # Clean up - rm nsis-3.12-strlen_8192.zip makensis -VERSION && echo # Should include defined symbol NSIS_MAX_STRLEN=8192 makensis -HDRINFO diff --git a/.stan.toml b/.stan.toml index 066f2ef802..299b627745 100644 --- a/.stan.toml +++ b/.stan.toml @@ -140,14 +140,14 @@ # Anti-pattern: Data.ByteString.Char8.pack [[ignore]] - id = "OBS-STAN-0203-erw24B-1079: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 # -# 1078 ┃ -# 1079 ┃ S8.pack . formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%S%6Q" -# 1080 ┃ ^^^^^^^ +# 1137 ┃ +# 1138 ┃ S8.pack . formatTime defaultTimeLocale "%Y-%m-%dT%H:%M:%S%6Q" +# 1139 ┃ ^^^^^^^ # Anti-pattern: Data.ByteString.Char8.pack [[ignore]] @@ -156,9 +156,9 @@ # ✦ Category: #AntiPattern # ✦ File: src\Stack\Build\ExecutePackage.hs # -# 249 ┃ -# 250 ┃ newConfigFileRoot <- S8.pack . toFilePath <$> view configFileRootL -# 251 ┃ ^^^^^^^ +# 251 ┃ +# 252 ┃ newConfigFileRoot <- S8.pack . toFilePath <$> view configFileRootL +# 253 ┃ ^^^^^^^ # Anti-pattern: Data.ByteString.Char8.pack [[ignore]] diff --git a/ChangeLog.md b/ChangeLog.md index c900df9116..705214044a 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,20 @@ # Changelog -## v3.11.1 - 2026-06-13 +## Unreleased changes + +Release notes: + +**Changes since v3.11.1:** + +Major changes: + +Behavior changes: + +Other enhancements: + +Bug fixes: + +## v3.11.1 - 2026-06-13 **Changes since v3.9.3:** diff --git a/cabal.config b/cabal.config index c76de115f5..d0ba24cd26 100644 --- a/cabal.config +++ b/cabal.config @@ -180,7 +180,7 @@ constraints: , silently ==1.2.5.4 , split ==0.2.5 , splitmix ==0.1.3.2 - , stack ==3.11.2 + , stack ==3.12.0 , static-bytes ==0.1.1 , stm ==2.5.3.1 , stm-chans ==3.0.0.11 diff --git a/package.yaml b/package.yaml index 9cbac5e3b7..47d3055feb 100644 --- a/package.yaml +++ b/package.yaml @@ -2,7 +2,7 @@ spec-version: 0.35.0 name: stack -version: '3.11.2' +version: '3.12.0' synopsis: A program for developing Haskell projects description: | Stack (the Haskell Tool Stack) is a program for developing Haskell projects. 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/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 d13be2be47..5ae611b9bb 100644 --- a/src/Stack/Build/ExecuteEnv.hs +++ b/src/Stack/Build/ExecuteEnv.hs @@ -79,7 +79,7 @@ import Stack.Prelude import Stack.Types.ApplyGhcOptions ( ApplyGhcOptions (..) ) import Stack.Types.Build ( ConvertPathsToAbsolute (..), ExcludeTHLoading (..) - , KeepOutputOpen (..) + , KeepOutputOpen (..), RunCabalWithArgs ) import Stack.Types.Build.Exception ( BuildException (..), BuildPrettyException (..) ) @@ -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,21 +599,25 @@ withSingleContext :: => ActionContext -> ExecuteEnv -> TaskType - -> Map PackageIdentifier GhcPkgId - -- ^ All dependencies' package ids to provide to Setup.hs. + -> 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. -> Maybe String + -- ^ An optional suffix for the build log's file name. -> ( Package -- Package info -> Path Abs File -- Cabal file path -> Path Abs Dir -- Package root directory file path -- Note that the `Path Abs Dir` argument is redundant with the -- `Path Abs File` argument, but we provide both to avoid recalculating -- `parent` of the `File`. - -> (KeepOutputOpen -> ExcludeTHLoading -> [String] -> RIO env ()) - -- Function to run Cabal with args + -> RunCabalWithArgs env + -- Function to run Cabal (the library) with arguments. -> (Utf8Builder -> RIO env ()) -- An plain 'announce' function, for different build phases -> OutputType - -> RIO env a) + -> RIO env a + ) -> RIO env a withSingleContext ac @@ -708,7 +715,8 @@ withSingleContext Package -> Path Abs Dir -> OutputType - -> ( (KeepOutputOpen -> ExcludeTHLoading -> [String] -> RIO env ()) + -> ( RunCabalWithArgs env + -- Function to run Cabal (the library) with arguments. -> RIO env a ) -> RIO env a @@ -795,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 @@ -875,6 +915,25 @@ withSingleContext setupArgs = ("--builddir=" ++ toFilePathNoTrailingSep distDir') : 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 903a647b77..233a9a56b9 100644 --- a/src/Stack/Build/ExecutePackage.hs +++ b/src/Stack/Build/ExecutePackage.hs @@ -88,6 +88,7 @@ import Stack.Package ) import Stack.PackageDump ( conduitDumpPackage, ghcPkgDescribe ) import Stack.Prelude +import Stack.Types.Build ( RunCabalWithArgs ) import Stack.Types.Build.Exception ( BuildException (..), BuildPrettyException (..) ) import Stack.Types.BuildConfig @@ -129,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 @@ -138,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 @@ -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 @@ -198,11 +198,11 @@ getConfigCache ee task installedMap enableTest enableBench = do -- collision for the return here. But unifying things with configureOpts -- where it was the opposite resulted in this. It doesn't seem to make any -- difference anyway. - allDepsMap = Map.union missing' task.present + allDeps = Map.union missing' task.present configureOpts' = configureOptsFromBase cOpts.envConfig cOpts.baseConfigOpts - allDepsMap + allDeps cOpts.isLocalNonExtraDep cOpts.isMutable pcOpts @@ -221,7 +221,7 @@ getConfigCache ee task installedMap enableTest enableBench = do , pkgSrc = task.cachePkgSrc , pathEnvVar = ee.pathEnvVar } - pure (allDepsMap, cache) + pure (allDeps, cache) -- | Ensure that the configuration for the package matches what is given ensureConfig :: @@ -384,7 +384,7 @@ singleBuild installedMap isFinalBuild = do - (allDepsMap, cache) <- + (allDeps, cache) <- getConfigCache ee task installedMap enableTests enableBenchmarks let bcoSnapInstallRoot = ee.baseConfigOpts.snapInstallRoot mprecompiled <- getPrecompiled cache task.taskType bcoSnapInstallRoot @@ -402,7 +402,7 @@ singleBuild (isFinalBuild, buildingFinals) cache curator - allDepsMap + allDeps whenJust minstalled $ \installed -> do writeFlagCache installed cache liftIO $ atomically $ modifyTVar ee.ghcPkgIds $ Map.insert pkgId installed @@ -424,7 +424,10 @@ 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. -> RIO env (Maybe Installed) realConfigAndBuild ac @@ -435,8 +438,8 @@ realConfigAndBuild (isFinalBuild, buildingFinals) cache mcurator0 - allDepsMap - = withSingleContext ac ee task.taskType allDepsMap Nothing $ + allDeps + = withSingleContext ac ee task.taskType allDeps Nothing $ \package cabalFP pkgDir cabal0 announce _outputType -> do let cabal = cabal0 CloseOnException _neededConfig <- @@ -508,9 +511,10 @@ realConfigAndBuild realBuild :: Package -> Path Abs Dir - -> (KeepOutputOpen -> ExcludeTHLoading -> [String] -> RIO env ()) + -> RunCabalWithArgs env + -- ^ Function to run Cabal (the library) with arguments. -> (Utf8Builder -> RIO env ()) - -- ^ A plain 'announce' function + -- ^ A plain 'announce' function. -> RIO env Installed realBuild package pkgDir cabal0 announce = do let cabal = cabal0 CloseOnException @@ -721,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) @@ -750,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 @@ -761,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. @@ -921,7 +924,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 @@ -994,11 +997,11 @@ singleTest :: singleTest topts testsToRun ac ee task installedMap = do -- FIXME: Since this doesn't use cabal, we should be able to avoid using a -- full blown 'withSingleContext'. - (allDepsMap, _cache) <- getConfigCache ee task installedMap True False + (allDeps, _cache) <- getConfigCache ee task installedMap True False mcurator <- view $ buildConfigL . to (.curator) let pname = pkgName $ taskProvides task expectFailure = expectTestFailure pname mcurator - withSingleContext ac ee task.taskType allDepsMap (Just "test") $ + withSingleContext ac ee task.taskType allDeps (Just "test") $ \package _cabalfp pkgDir _cabal announce outputType -> do config <- view configL let needHpc = topts.coverage @@ -1062,8 +1065,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) @@ -1103,7 +1106,7 @@ singleTest topts testsToRun ac ee task installedMap = do <> display (ghcPkgIdToText ghcId) <> "\n" ) - (pkgGhcIdList ++ thGhcId:Map.elems allDepsMap) + (pkgGhcIdList ++ thGhcId : Map.elems allDeps) writeFileUtf8Builder fp ghcEnv menv <- liftIO $ setEnv fp =<< config.processContextSettings EnvSettings @@ -1302,8 +1305,8 @@ singleBench :: -> InstalledMap -> RIO env () singleBench beopts benchesToRun ac ee task installedMap = do - (allDepsMap, _cache) <- getConfigCache ee task installedMap False True - withSingleContext ac ee task.taskType allDepsMap (Just "bench") $ + (allDeps, _cache) <- getConfigCache ee task installedMap False True + withSingleContext ac ee task.taskType allDeps (Just "bench") $ \_package _cabalfp _pkgDir cabal announce _outputType -> do let args = map unqualCompToString benchesToRun <> maybe [] ((:[]) . ("--benchmark-options=" <>)) diff --git a/src/Stack/Build/Installed.hs b/src/Stack/Build/Installed.hs index 1e2d4d5b45..609175df4d 100644 --- a/src/Stack/Build/Installed.hs +++ b/src/Stack/Build/Installed.hs @@ -287,7 +287,7 @@ 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) mempty toInstallLocation :: PackageDbVariety -> InstallLocation toInstallLocation GlobalDb = Snap @@ -313,23 +313,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/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/Ghci.hs b/src/Stack/Ghci.hs index 8db0e71196..d6cca1d93a 100644 --- a/src/Stack/Ghci.hs +++ b/src/Stack/Ghci.hs @@ -182,6 +182,7 @@ data GhciPkgDesc = GhciPkgDesc { package :: !Package , cabalFP :: !(Path Abs File) , target :: !Target + -- ^ How the package is intended to be built. } -- | Type synonym representing maps from a module name to a map with all of the @@ -292,8 +293,12 @@ ghci opts = do localTargets mainFile pkgs + -- Files targets with unknown GHC options: (maybe [] snd mfileTargets) + -- The names of packages to be exposed: (nonLocalTargets ++ addPkgs) + -- A map of package names and sequences of their sublibrary components + -- depended upon (if any): relevantDependencies preprocessTargets :: @@ -301,6 +306,7 @@ preprocessTargets :: => BuildOptsCLI -> SMActual GlobalPackage -> [Text] + -- ^ Raw (unprocessed) targets from the command line. -> RIO env (Either [Path Abs File] (Map PackageName Target)) preprocessTargets buildOptsCLI sma rawTargets = do let (fileTargetsRaw, normalTargetsRaw) = @@ -349,7 +355,12 @@ findFileTargets :: HasEnvConfig env => [LocalPackage] -> [Path Abs File] - -> RIO env (Map PackageName Target, Map PackageName [Path Abs File], [Path Abs File]) + -> RIO + env + ( Map PackageName Target + , Map PackageName [Path Abs File] + , [Path Abs File] + ) findFileTargets locals fileTargets = do filePackages <- forM locals $ \lp -> do PackageComponentFile _ compFiles _ _ <- getPackageFile lp.package lp.cabalFP @@ -486,10 +497,17 @@ runGhci :: => GhciOpts -> [(PackageName, (Path Abs File, Target))] -> Maybe (Path Abs File) + -- ^ Path to source file for selected main module. 'Nothing' if no main + -- module is to be loaded and imported. -> [GhciPkgInfo] -> [Path Abs File] + -- ^ Files targets with unknown GHC options. -> [PackageName] + -- ^ The names of packages to be exposed. -> Map PackageName (Seq NamedComponent) + -- ^ A map of package names and their sublibraries depended on (if + -- any), the package names to be exposed if any sublibraries are + -- depended on. -> RIO env () runGhci ghciOpts @@ -650,21 +668,31 @@ writeHashedFile outputDirectory relFile contents = do renderScript :: [GhciPkgInfo] -> Maybe (Path Abs File) + -- ^ Path to source file for selected main module. 'Nothing' if no main + -- module is to be loaded and imported. -> Bool + -- ^ Only load and import the main module? -> [Path Abs File] + -- ^ Files targets with unknown GHC options. -> GhciScript renderScript pkgs mainFile onlyMain extraFiles = do - let addPhase = cmdAdd $ S.fromList (map Left allModules ++ addMain) - addMain = maybe [] (L.singleton . Right) mainFile - modulePhase = cmdModule $ S.fromList allModules - allModules = nubOrd $ concatMap (M.keys . (.modules)) pkgs + let allModules = S.unions $ map (M.keysSet . (.modules)) pkgs + addMain = maybe S.empty (S.singleton . Right) mainFile + -- If a main module is to be :add-ed, the context will be set to + -- it: + addPhase = cmdAdd $ S.map Left allModules <> addMain + modulePhase = cmdModule allModules case getFileTargets pkgs <> extraFiles of [] -> if onlyMain then if isJust mainFile - then cmdAdd (S.fromList addMain) - else mempty + then + -- If a main module is to be :add-ed, the context will be set to + -- it: + cmdAdd addMain + else + mempty else addPhase <> modulePhase fileTargets -> cmdAdd (S.fromList (map Right fileTargets)) diff --git a/src/Stack/Ghci/Script.hs b/src/Stack/Ghci/Script.hs index 0379087e61..36b639b25d 100644 --- a/src/Stack/Ghci/Script.hs +++ b/src/Stack/Ghci/Script.hs @@ -35,7 +35,15 @@ instance Monoid GhciScript where data GhciCommand = AddCmd (Set (Either ModuleName (Path Abs File))) + -- ^ Add the specified modules (specified by module name or source file) to + -- the current target set and perform a reload (that is, load the target set + -- of modules and the all the modules they depend on in dependency order). + -- The modules specified by name are added first (in ascending order), then + -- the modules specified by source file (in ascending order). The context is + -- set to the most recently successfully loaded module. | ModuleCmd (Set ModuleName) + -- ^ Add the specified modules to the context. The modules are added in + -- ascending order. deriving Show cmdAdd :: Set (Either ModuleName (Path Abs File)) -> GhciScript diff --git a/src/Stack/Package.hs b/src/Stack/Package.hs index aac9443d09..590262d106 100644 --- a/src/Stack/Package.hs +++ b/src/Stack/Package.hs @@ -571,6 +571,7 @@ buildLogPath :: (MonadReader env m, HasBuildConfig env, MonadThrow m) => Package -> Maybe String + -- ^ An optional suffix for the log's file name. -> m (Path Abs File) buildLogPath package' msuffix = do env <- ask @@ -817,6 +818,7 @@ setOfPackageDeps pkg = runIdentity $ topSortPackageComponent :: Package -> Target + -- ^ How the package is intended to be built. -> Bool -- ^ Include directTarget or not. False here means we won't include the -- actual targets in the result, only their deps. Using it with False here @@ -844,6 +846,7 @@ topProcessPackageComponent :: forall b. Package -> Target + -- ^ How the package is intended to be built. -> ( forall component. (HasComponentInfo component) => PackageType -> component diff --git a/src/Stack/SDist.hs b/src/Stack/SDist.hs index a27f117ad6..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 (..) ) @@ -204,8 +203,7 @@ getSDistTarball :: -- ^ Filename, tarball contents, and option Cabal file revision to upload getSDistTarball mpvpBounds pkgDir = do config <- view configL - let PvpBounds pvpBounds asRevision = - fromMaybe config.pvpBounds mpvpBounds + let PvpBounds pvpBounds asRevision = fromMaybe config.pvpBounds mpvpBounds tweakCabal = pvpBounds /= PvpBoundsNone pkgFp = toFilePath pkgDir lp <- readLocalPackage pkgDir @@ -226,14 +224,13 @@ getSDistTarball mpvpBounds pkgDir = do installMap <- toInstallMap sourceMap (installedMap, _globalDumpPkgs, _snapshotDumpPkgs, _localDumpPkgs) <- getInstalled installMap - let deps = Map.fromList - [ (pid, libInfo.ghcPkgId) - | (_, Library pid libInfo) <- 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,19 +474,22 @@ 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. -> RIO env (String, Path Abs File) -getSDistFileList lp deps = +getSDistFileList lp allDeps = withSystemTempDir (stackProgName <> "-sdist") $ \tmpdir -> do let bopts = defaultBuildOpts let boptsCli = defaultBuildOptsCLI baseConfigOpts <- mkBaseConfigOpts boptsCli locals <- projectLocalPackages - withExecuteEnv bopts boptsCli baseConfigOpts locals - [] [] [] Nothing -- provide empty list of globals. This is a hack around - -- custom Setup.hs files + -- We provide three empty lists of dumped installed packages. This is a hack + -- around custom Setup.hs files: + withExecuteEnv bopts boptsCli baseConfigOpts locals [] [] [] Nothing $ \ee -> - withSingleContext ac ee taskType deps (Just "sdist") $ + withSingleContext ac ee taskType allDeps (Just "sdist") $ \_package cabalFP _pkgDir cabal _announce _outputType -> do let outFile = toFilePath tmpdir FP. "source-files-list" cabal diff --git a/src/Stack/Types/Build.hs b/src/Stack/Types/Build.hs index ea64c0fe0b..9bae8b5265 100644 --- a/src/Stack/Types/Build.hs +++ b/src/Stack/Types/Build.hs @@ -9,13 +9,19 @@ Build-specific types. -} module Stack.Types.Build - ( ExcludeTHLoading (..) + ( RunCabalWithArgs + , ExcludeTHLoading (..) , ConvertPathsToAbsolute (..) , KeepOutputOpen (..) ) where import Stack.Prelude +-- | Type synonym that represents functions that run Cabal (the library) with +-- arguments. +type RunCabalWithArgs env = + KeepOutputOpen -> ExcludeTHLoading -> [String] -> RIO env () + -- | Type representing treatments of GHC's informational messages during -- compilation when it evaluates Template Haskell code. data ExcludeTHLoading 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/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/GhciOpts.hs b/src/Stack/Types/GhciOpts.hs index 15b9f322f7..17c6ecff07 100644 --- a/src/Stack/Types/GhciOpts.hs +++ b/src/Stack/Types/GhciOpts.hs @@ -20,6 +20,7 @@ import Stack.Types.BuildOptsCLI ( ApplyCLIFlag (..) ) -- commands. data GhciOpts = GhciOpts { targets :: ![Text] + -- ^ Raw (unprocessed) targets from the command line. , args :: ![String] , ghcOptions :: ![String] , flags :: !(Map ApplyCLIFlag (Map FlagName Bool)) @@ -31,5 +32,6 @@ data GhciOpts = GhciOpts , hidePackages :: !(Maybe Bool) , noBuild :: !Bool , onlyMain :: !Bool + -- ^ Only load and import the main module? } deriving Show diff --git a/src/Stack/Types/Installed.hs b/src/Stack/Types/Installed.hs index ba84f66b1e..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 ) @@ -101,37 +99,43 @@ 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 - , license :: Maybe (Either SPDX.License License) + { mMainGhcPkgId :: Maybe GhcPkgId + -- ^ The main library, if present. If absent, there must be one or more + -- installed sublibraries. , subLib :: Map StackUnqualCompName GhcPkgId + -- ^ If there are no sublibraries, there must be a main library. } deriving (Eq, Show) -- | 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 ghcPkgId Nothing mempty + InstalledLibraryInfo (Just ghcPkgId) 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 +simpleInstalledLib pkgIdentifier mMainGhcPkgId = + Library pkgIdentifier . InstalledLibraryInfo mMainGhcPkgId 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 +145,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..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,7 +41,7 @@ module Stack.Types.Package , packageIdentifier , psVersion , runMemoizedWith - , simpleInstalledLib + , toCabalMungedPackageId , toCabalMungedPackageName , toPackageDbVariety ) where @@ -54,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 ) @@ -74,7 +73,7 @@ import Stack.Types.Installed ( InstallLocation (..), InstallMap, Installed (..) , InstalledLibraryInfo (..), InstalledMap , InstalledPackageLocation (..), PackageDatabase (..) - , PackageDbVariety(..), simpleInstalledLib + , PackageDbVariety(..), installedPackageIdentifier , toPackageDbVariety ) import Stack.Types.NamedComponent ( NamedComponent ) @@ -386,43 +385,57 @@ 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 + -- ^ 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 = M.insert pkgId installedLib.ghcPkgId 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/stack.cabal b/stack.cabal index 6f173ade3e..9ea64af05e 100644 --- a/stack.cabal +++ b/stack.cabal @@ -5,7 +5,7 @@ cabal-version: 2.2 -- see: https://github.com/sol/hpack name: stack -version: 3.11.2 +version: 3.12.0 synopsis: A program for developing Haskell projects description: Stack (the Haskell Tool Stack) is a program for developing Haskell projects. It is aimed at new and experienced users of Haskell and seeks to support them 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 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