module Pier.Build.TargetInfo ( TargetInfo(..) , TargetResult(..) , TransitiveDeps(..) , getTargetInfo ) where import Control.Monad (filterM) import Data.List (nub) import Data.Maybe (catMaybes, fromMaybe) import Development.Shake import Development.Shake.FilePath (()) import Distribution.Compiler (CompilerFlavor(GHC)) import Distribution.ModuleName import Distribution.Package (packageName) import Distribution.PackageDescription import Distribution.Text (display) import Language.Haskell.Extension import Pier.Build.CFlags import Pier.Build.ConfiguredPackage import Pier.Build.Module import Pier.Build.Stackage import Pier.Core.Artifact data TargetInfo = TargetInfo { targetCFlags :: CFlags , targetSourceInputs :: [Artifact] -- ^ Source files to pass on command line , targetOtherInputs :: [Artifact] -- ^ Other files to pass on command line , targetOptions :: [String] , targetIncludeDirs :: [FilePath] -- ^ Directories in which GHC should look for includes -- TODO: merge with CFlags? Make Artifact? , targetSourceDirs :: [Artifact] -- ^ Directories in which GHC should look for boot files , targetOtherModules :: [ModuleName] } data TargetResult = TargetBinary { targetModulePath :: FilePath } | TargetLibrary { targetExposedModules :: [ModuleName] } getTargetInfo :: ConfiguredPackage -> BuildInfo -> TargetResult -> TransitiveDeps -> InstalledGhc -> Action TargetInfo getTargetInfo confd bi result deps ghc = do let packageSourceDir = confdSourceDir confd cflags <- getCFlags deps packageSourceDir bi let allOptions = map ("-X" ++) (display (fromMaybe Haskell98 $ defaultLanguage bi) : map display (defaultExtensions bi ++ oldExtensions bi)) ++ concat [opts | (GHC,opts) <- options bi] let srcDirs = sourceDirArtifacts packageSourceDir bi let fixDashes = map $ \c -> if c == '-' then '_' else c let pathsMod = fromString $ "Paths_" ++ fixDashes (display $ packageName confd) let allModules = otherModules bi ++ case result of TargetLibrary exposed -> exposed TargetBinary _ -- Add the Paths_ module automatically to other-modules -- of binaries. -- TODO: consider whether this is intended behavior of Cabal. -> [pathsMod | pathsMod `notElem` otherModules bi] moduleFiles <- mapM (findModule ghc confd cflags srcDirs) allModules moduleBootFiles <- catMaybes <$> mapM findBootFile moduleFiles let cFiles = map (packageSourceDir />) $ cSources bi cIncludes <- collectCIncludes (confdDesc confd) bi (packageSourceDir />) moduleMainFiles <- case result of TargetLibrary{} -> return [] TargetBinary f -> do path <- findMainFile ghc cflags srcDirs f return [path | path `notElem` moduleFiles] return TargetInfo { targetCFlags = cflags , targetSourceInputs = cFiles ++ moduleFiles ++ moduleMainFiles , targetOtherInputs = cIncludes ++ moduleBootFiles , targetOptions = allOptions , targetIncludeDirs = includeDirs bi , targetSourceDirs = srcDirs , targetOtherModules = otherModules bi } collectCIncludes :: PackageDescription -> BuildInfo -> (FilePath -> Artifact) -> Action [Artifact] collectCIncludes desc bi pkgDir = do includeInputs <- findIncludeInputs pkgDir bi extraTmps <- fmap catMaybes . mapM ((\f -> doesArtifactExist f >>= \case True -> return (Just f) False -> return Nothing) . pkgDir) $ extraTmpFiles desc return $ includeInputs ++ extraTmps findIncludeInputs :: (FilePath -> Artifact) -> BuildInfo -> Action [Artifact] findIncludeInputs pkgDir bi = filterM doesArtifactExist candidates where candidates = nub -- TODO: more efficient [ pkgDir $ d f -- TODO: maybe just installIncludes shouldn't be prefixed -- with include dir? | d <- "" : includeDirs bi , f <- includes bi ++ installIncludes bi ]