stack-2.11.1: src/Stack/SourceMap.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Stack.SourceMap
( mkProjectPackage
, snapToDepPackage
, additionalDepPackage
, loadVersion
, getPLIVersion
, loadGlobalHints
, DumpedGlobalPackage
, actualFromGhc
, actualFromHints
, checkFlagsUsedThrowing
, globalCondCheck
, pruneGlobals
, globalsFromHints
, getCompilerInfo
, immutableLocSha
, loadProjectSnapshotCandidate
, SnapshotCandidate
, globalsFromDump
) where
import Data.ByteString.Builder ( byteString )
import qualified Data.Conduit.List as CL
import qualified Data.Text as T
import qualified Distribution.PackageDescription as PD
import Distribution.System ( Platform (..) )
import qualified Pantry.SHA256 as SHA256
import qualified RIO.Map as Map
import RIO.Process ( HasProcessContext )
import qualified RIO.Set as Set
import Stack.Constants ( stackProgName' )
import Stack.PackageDump ( conduitDumpPackage, ghcPkgDump )
import Stack.Prelude
import Stack.Types.Build.Exception ( BuildPrettyException (..) )
import Stack.Types.Compiler
( ActualCompiler, actualToWanted, wantedToActual )
import Stack.Types.CompilerPaths
( CompilerPaths (..), GhcPkgExe, HasCompiler (..) )
import Stack.Types.Config ( HasConfig )
import Stack.Types.DumpPackage ( DumpPackage (..) )
import Stack.Types.UnusedFlags ( FlagSource, UnusedFlags (..) )
import Stack.Types.Platform ( HasPlatform (..) )
import Stack.Types.Runner ( rslInLogL )
import Stack.Types.SourceMap
( CommonPackage (..), DepPackage (..), FromSnapshot (..)
, GlobalPackage (..), GlobalPackageVersion (..)
, ProjectPackage (..), SMActual (..), SMWanted (..)
)
-- | Create a 'ProjectPackage' from a directory containing a package.
mkProjectPackage ::
forall env. (HasPantryConfig env, HasLogFunc env, HasProcessContext env)
=> PrintWarnings
-> ResolvedPath Dir
-> Bool
-> RIO env ProjectPackage
mkProjectPackage printWarnings dir buildHaddocks = do
(gpd, name, cabalfp) <-
loadCabalFilePath (Just stackProgName') (resolvedAbsolute dir)
pure ProjectPackage
{ ppCabalFP = cabalfp
, ppResolvedDir = dir
, ppCommon =
CommonPackage
{ cpGPD = gpd printWarnings
, cpName = name
, cpFlags = mempty
, cpGhcOptions = mempty
, cpCabalConfigOpts = mempty
, cpHaddocks = buildHaddocks
}
}
-- | Create a 'DepPackage' from a 'PackageLocation', from some additional
-- to a snapshot setting (extra-deps or command line)
additionalDepPackage ::
forall env. (HasPantryConfig env, HasLogFunc env, HasProcessContext env)
=> Bool
-> PackageLocation
-> RIO env DepPackage
additionalDepPackage buildHaddocks pl = do
(name, gpdio) <-
case pl of
PLMutable dir -> do
(gpdio, name, _cabalfp) <-
loadCabalFilePath (Just stackProgName') (resolvedAbsolute dir)
pure (name, gpdio NoPrintWarnings)
PLImmutable pli -> do
let PackageIdentifier name _ = packageLocationIdent pli
run <- askRunInIO
pure (name, run $ loadCabalFileImmutable pli)
pure DepPackage
{ dpLocation = pl
, dpHidden = False
, dpFromSnapshot = NotFromSnapshot
, dpCommon =
CommonPackage
{ cpGPD = gpdio
, cpName = name
, cpFlags = mempty
, cpGhcOptions = mempty
, cpCabalConfigOpts = mempty
, cpHaddocks = buildHaddocks
}
}
snapToDepPackage ::
forall env. (HasPantryConfig env, HasLogFunc env, HasProcessContext env)
=> Bool
-> PackageName
-> SnapshotPackage
-> RIO env DepPackage
snapToDepPackage buildHaddocks name SnapshotPackage{..} = do
run <- askRunInIO
pure DepPackage
{ dpLocation = PLImmutable spLocation
, dpHidden = spHidden
, dpFromSnapshot = FromSnapshot
, dpCommon =
CommonPackage
{ cpGPD = run $ loadCabalFileImmutable spLocation
, cpName = name
, cpFlags = spFlags
, cpGhcOptions = spGhcOptions
, cpCabalConfigOpts = [] -- No spCabalConfigOpts, not present in snapshots
, cpHaddocks = buildHaddocks
}
}
loadVersion :: MonadIO m => CommonPackage -> m Version
loadVersion common = do
gpd <- liftIO $ cpGPD common
pure (pkgVersion $ PD.package $ PD.packageDescription gpd)
getPLIVersion :: PackageLocationImmutable -> Version
getPLIVersion (PLIHackage (PackageIdentifier _ v) _ _) = v
getPLIVersion (PLIArchive _ pm) = pkgVersion $ pmIdent pm
getPLIVersion (PLIRepo _ pm) = pkgVersion $ pmIdent pm
globalsFromDump ::
(HasProcessContext env, HasTerm env)
=> GhcPkgExe
-> RIO env (Map PackageName DumpedGlobalPackage)
globalsFromDump pkgexe = do
let pkgConduit = conduitDumpPackage
.| CL.foldMap (\dp -> Map.singleton (dpGhcPkgId dp) dp)
toGlobals ds =
Map.fromList $ map (pkgName . dpPackageIdent &&& id) $ Map.elems ds
toGlobals <$> ghcPkgDump pkgexe [] pkgConduit
globalsFromHints ::
HasConfig env
=> WantedCompiler
-> RIO env (Map PackageName Version)
globalsFromHints compiler = do
mglobalHints <- loadGlobalHints compiler
case mglobalHints of
Just hints -> pure hints
Nothing -> do
prettyWarnL
[ flow "Unable to load global hints for"
, fromString $ T.unpack $ textDisplay compiler
]
pure mempty
type DumpedGlobalPackage = DumpPackage
actualFromGhc ::
(HasConfig env, HasCompiler env)
=> SMWanted
-> ActualCompiler
-> RIO env (SMActual DumpedGlobalPackage)
actualFromGhc smw ac = do
globals <- view $ compilerPathsL.to cpGlobalDump
pure
SMActual
{ smaCompiler = ac
, smaProject = smwProject smw
, smaDeps = smwDeps smw
, smaGlobal = globals
}
actualFromHints ::
(HasConfig env)
=> SMWanted
-> ActualCompiler
-> RIO env (SMActual GlobalPackageVersion)
actualFromHints smw ac = do
globals <- globalsFromHints (actualToWanted ac)
pure
SMActual
{ smaCompiler = ac
, smaProject = smwProject smw
, smaDeps = smwDeps smw
, smaGlobal = Map.map GlobalPackageVersion globals
}
-- | Simple cond check for boot packages - checks only OS and Arch
globalCondCheck ::
(HasConfig env)
=> RIO env (PD.ConfVar
-> Either PD.ConfVar Bool)
globalCondCheck = do
Platform arch os <- view platformL
let condCheck (PD.OS os') = pure $ os' == os
condCheck (PD.Arch arch') = pure $ arch' == arch
condCheck c = Left c
pure condCheck
checkFlagsUsedThrowing ::
(MonadIO m, MonadThrow m)
=> Map PackageName (Map FlagName Bool)
-> FlagSource
-> Map PackageName ProjectPackage
-> Map PackageName DepPackage
-> m ()
checkFlagsUsedThrowing packageFlags source prjPackages deps = do
unusedFlags <-
forMaybeM (Map.toList packageFlags) $ \(pname, flags) ->
getUnusedPackageFlags (pname, flags) source prjPackages deps
unless (null unusedFlags) $
prettyThrowM $ InvalidFlagSpecification $ Set.fromList unusedFlags
getUnusedPackageFlags ::
MonadIO m
=> (PackageName, Map FlagName Bool)
-> FlagSource
-> Map PackageName ProjectPackage
-> Map PackageName DepPackage
-> m (Maybe UnusedFlags)
getUnusedPackageFlags (name, userFlags) source prj deps =
let maybeCommon = fmap ppCommon (Map.lookup name prj)
<|> fmap dpCommon (Map.lookup name deps)
in case maybeCommon of
-- Package is not available as project or dependency
Nothing ->
pure $ Just $ UFNoPackage source name
-- Package exists, let's check if the flags are defined
Just common -> do
gpd <- liftIO $ cpGPD common
let pname = pkgName $ PD.package $ PD.packageDescription gpd
pkgFlags = Set.fromList $ map PD.flagName $ PD.genPackageFlags gpd
unused = Map.keysSet $ Map.withoutKeys userFlags pkgFlags
if Set.null unused
-- All flags are defined, nothing to do
then pure Nothing
-- Error about the undefined flags
else pure $ Just $ UFFlagsNotDefined source pname pkgFlags unused
pruneGlobals ::
Map PackageName DumpedGlobalPackage
-> Set PackageName
-> Map PackageName GlobalPackage
pruneGlobals globals deps =
let (prunedGlobals, keptGlobals) =
partitionReplacedDependencies globals (pkgName . dpPackageIdent)
dpGhcPkgId dpDepends deps
in Map.map (GlobalPackage . pkgVersion . dpPackageIdent) keptGlobals <>
Map.map ReplacedGlobalPackage prunedGlobals
getCompilerInfo :: (HasConfig env, HasCompiler env) => RIO env Builder
getCompilerInfo = view $ compilerPathsL.to (byteString . cpGhcInfo)
immutableLocSha :: PackageLocationImmutable -> Builder
immutableLocSha = byteString . treeKeyToBs . locationTreeKey
where
locationTreeKey (PLIHackage _ _ tk) = tk
locationTreeKey (PLIArchive _ pm) = pmTreeKey pm
locationTreeKey (PLIRepo _ pm) = pmTreeKey pm
treeKeyToBs (TreeKey (BlobKey sha _)) = SHA256.toHexBytes sha
type SnapshotCandidate env
= [ResolvedPath Dir] -> RIO env (SMActual GlobalPackageVersion)
loadProjectSnapshotCandidate ::
(HasConfig env)
=> RawSnapshotLocation
-> PrintWarnings
-> Bool
-> RIO env (SnapshotCandidate env)
loadProjectSnapshotCandidate loc printWarnings buildHaddocks = do
debugRSL <- view rslInLogL
(snapshot, _, _) <- loadAndCompleteSnapshotRaw' debugRSL loc Map.empty Map.empty
deps <- Map.traverseWithKey (snapToDepPackage False) (snapshotPackages snapshot)
let wc = snapshotCompiler snapshot
globals <- Map.map GlobalPackageVersion <$> globalsFromHints wc
pure $ \projectPackages -> do
prjPkgs <- fmap Map.fromList . for projectPackages $ \resolved -> do
pp <- mkProjectPackage printWarnings resolved buildHaddocks
pure (cpName $ ppCommon pp, pp)
compiler <- either throwIO pure $ wantedToActual $ snapshotCompiler snapshot
pure
SMActual
{ smaCompiler = compiler
, smaProject = prjPkgs
, smaDeps = Map.difference deps prjPkgs
, smaGlobal = globals
}