@@ -17,6 +17,7 @@ module Stack.Build.Execute
1717 ) where
1818
1919import Control.Applicative
20+ import Control.Arrow ((&&&) )
2021import Control.Concurrent.Execute
2122import Control.Concurrent.Async (withAsync , wait )
2223import Control.Concurrent.MVar.Lifted
@@ -69,6 +70,7 @@ import Stack.Types.Build
6970import Stack.Fetch as Fetch
7071import Stack.GhcPkg
7172import Stack.Package
73+ import Stack.PackageDump
7274import Stack.Constants
7375import Stack.Types
7476import Stack.Types.StackT
@@ -205,7 +207,7 @@ data ExecuteEnv = ExecuteEnv
205207 , eeLocals :: ! [LocalPackage ]
206208 , eeSourceMap :: ! SourceMap
207209 , eeGlobalDB :: ! (Path Abs Dir )
208- , eeGlobalPackages :: ! ( Map PackageIdentifier GhcPkgId )
210+ , eeGlobalPackages :: ! [ DumpPackage () () ]
209211 }
210212
211213-- | Get a compiled Setup exe
@@ -278,7 +280,7 @@ withExecuteEnv :: M env m
278280 -> BuildOpts
279281 -> BaseConfigOpts
280282 -> [LocalPackage ]
281- -> Map PackageIdentifier GhcPkgId -- ^ global packages
283+ -> [ DumpPackage () () ] -- ^ global packages
282284 -> SourceMap
283285 -> (ExecuteEnv -> m a )
284286 -> m a
@@ -322,7 +324,7 @@ executePlan :: M env m
322324 -> BuildOpts
323325 -> BaseConfigOpts
324326 -> [LocalPackage ]
325- -> Map PackageIdentifier GhcPkgId -- ^ globals
327+ -> [ DumpPackage () () ] -- ^ globals
326328 -> SourceMap
327329 -> InstalledMap
328330 -> Plan
@@ -701,19 +703,10 @@ withSingleContext runInBase ActionContext {..} ExecuteEnv {..} task@Task {..} md
701703 Just deps ->
702704 -- Stack always builds with the global Cabal for various
703705 -- reproducibility issues.
704- let depsMinusCabal = filter (not . isPrefixOf " Cabal-" )
705- . map ghcPkgIdString
706- . Map. elems
707- . uniqueByName
708- $ Map. union deps eeGlobalPackages
709- -- We also provide all global packages to
710- -- the Setup.hs file, see:
711- -- https://github.com/commercialhaskell/stack/issues/941
712-
713- -- We only want a single installation of each package name
714- uniqueByName = Map. mapKeysWith
715- const
716- packageIdentifierName
706+ let depsMinusCabal
707+ = map ghcPkgIdString
708+ $ Set. toList
709+ $ addGlobalPackages deps eeGlobalPackages
717710 in
718711 " -clear-package-db"
719712 : " -global-package-db"
@@ -1289,3 +1282,71 @@ extraBuildOptions :: M env m => m [String]
12891282extraBuildOptions = do
12901283 hpcIndexDir <- toFilePath . (</> dotHpc) <$> hpcRelativeDir
12911284 return [" --ghc-options" , " -hpcdir " ++ hpcIndexDir ++ " -ddump-hi -ddump-to-file" ]
1285+
1286+ -- | Take the given list of package dependencies and the contents of the global
1287+ -- package database, and construct a set of installed package IDs that:
1288+ --
1289+ -- * Excludes the Cabal library (it's added later)
1290+ --
1291+ -- * Includes all packages depended on by this package
1292+ --
1293+ -- * Includes all global packages, unless: (1) it's hidden, (2) it's shadowed
1294+ -- by a depended-on package, or (3) one of its dependencies is not met.
1295+ --
1296+ -- See:
1297+ --
1298+ -- * https://github.com/commercialhaskell/stack/issues/941
1299+ --
1300+ -- * https://github.com/commercialhaskell/stack/issues/944
1301+ --
1302+ -- * https://github.com/commercialhaskell/stack/issues/949
1303+ addGlobalPackages :: Map PackageIdentifier GhcPkgId -- ^ dependencies of the package
1304+ -> [DumpPackage () () ] -- ^ global packages
1305+ -> Set GhcPkgId
1306+ addGlobalPackages deps globals0 =
1307+ res
1308+ where
1309+ -- Initial set of packages: the installed IDs of all dependencies
1310+ res0 = Map. elems $ Map. filterWithKey (\ ident _ -> not $ isCabal ident) deps
1311+
1312+ -- First check on globals: it's not shadowed by a dep, it's not Cabal, and
1313+ -- it's exposed
1314+ goodGlobal1 dp = not (isDep dp)
1315+ && not (isCabal $ dpPackageIdent dp)
1316+ && dpIsExposed dp
1317+ globals1 = filter goodGlobal1 globals0
1318+
1319+ -- Create a Map of unique package names in the global database
1320+ globals2 = Map. fromListWith chooseBest
1321+ $ map (packageIdentifierName . dpPackageIdent &&& id ) globals1
1322+
1323+ -- Final result: add in globals that have their dependencies met
1324+ res = loop id (Map. elems globals2) $ Set. fromList res0
1325+
1326+ ----------------------------------
1327+ -- Some auxiliary helper functions
1328+ ----------------------------------
1329+
1330+ -- Is the given package identifier for any version of Cabal
1331+ isCabal (PackageIdentifier name _) = name == $ (mkPackageName " Cabal" )
1332+
1333+ -- Is the given package name provided by the package dependencies?
1334+ isDep dp = packageIdentifierName (dpPackageIdent dp) `Set.member` depNames
1335+ depNames = Set. map packageIdentifierName $ Map. keysSet deps
1336+
1337+ -- Choose the best of two competing global packages (the newest version)
1338+ chooseBest dp1 dp2
1339+ | getVer dp1 < getVer dp2 = dp2
1340+ | otherwise = dp1
1341+ where
1342+ getVer = packageIdentifierVersion . dpPackageIdent
1343+
1344+ -- Are all dependencies of the given package met by the given Set of
1345+ -- installed packages
1346+ depsMet dp gids = all (`Set.member` gids) (dpDepends dp)
1347+
1348+ -- Find all globals that have all of their dependencies met
1349+ loop _ [] gids = gids
1350+ loop front (dp: dps) gids
1351+ | depsMet dp gids = loop id (front dps) (Set. insert (dpGhcPkgId dp) gids)
1352+ | otherwise = loop (front . (dp: )) dps gids
0 commit comments