Cabal-3.12.0.0: src/Distribution/Simple/GHC/Build/ExtraSources.hs
{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
module Distribution.Simple.GHC.Build.ExtraSources where
import Control.Monad
import Data.Foldable
import Distribution.Simple.Flag
import qualified Distribution.Simple.GHC.Internal as Internal
import Distribution.Simple.Program.GHC
import Distribution.Simple.Utils
import Distribution.Utils.NubList
import Distribution.Types.BuildInfo
import Distribution.Types.Component
import Distribution.Types.TargetInfo
import Distribution.Simple.GHC.Build.Utils
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.Program.Types
import Distribution.System (Arch (JavaScript), Platform (..))
import Distribution.Types.ComponentLocalBuildInfo
import Distribution.Types.Executable
import Distribution.Verbosity (Verbosity)
import Distribution.Simple.Build.Inputs
-- | An action that builds all the extra build sources of a component, i.e. C,
-- C++, Js, Asm, C-- sources.
buildAllExtraSources
:: ConfiguredProgram
-- ^ The GHC configured program
-> FilePath
-- ^ The build directory for this target
-> PreBuildComponentInputs
-- ^ The context and component being built in it.
-> IO (NubListR FilePath)
-- ^ Returns the (nubbed) list of extra sources that were built
buildAllExtraSources =
mconcat
[ buildCSources
, buildCxxSources
, buildJsSources
, buildAsmSources
, buildCmmSources
]
buildCSources
, buildCxxSources
, buildJsSources
, buildAsmSources
, buildCmmSources
:: ConfiguredProgram
-- ^ The GHC configured program
-> FilePath
-- ^ The build directory for this target
-> PreBuildComponentInputs
-- ^ The context and component being built in it.
-> IO (NubListR FilePath)
-- ^ Returns the list of extra sources that were built
buildCSources =
buildExtraSources
"C Sources"
Internal.componentCcGhcOptions
True
( \c ->
cSources (componentBuildInfo c)
++ case c of
CExe exe | isC (modulePath exe) -> [modulePath exe]
_otherwise -> []
)
buildCxxSources =
buildExtraSources
"C++ Sources"
Internal.componentCxxGhcOptions
True
( \c ->
cxxSources (componentBuildInfo c)
++ case c of
CExe exe | isCxx (modulePath exe) -> [modulePath exe]
_otherwise -> []
)
buildJsSources ghcProg buildTargetDir = do
Platform hostArch _ <- hostPlatform <$> localBuildInfo
let hasJsSupport = hostArch == JavaScript
buildExtraSources
"JS Sources"
Internal.componentJsGhcOptions
False
( \c ->
if hasJsSupport
then -- JS files are C-like with GHC's JS backend: they are
-- "compiled" into `.o` files (renamed with a header).
-- This is a difference from GHCJS, for which we only
-- pass the JS files at link time.
jsSources (componentBuildInfo c)
else mempty
)
ghcProg
buildTargetDir
buildAsmSources =
buildExtraSources
"Assembler Sources"
Internal.componentAsmGhcOptions
True
(asmSources . componentBuildInfo)
buildCmmSources =
buildExtraSources
"C-- Sources"
Internal.componentCmmGhcOptions
True
(cmmSources . componentBuildInfo)
-- | Create 'PreBuildComponentRules' for a given type of extra build sources
-- which are compiled via a GHC invocation with the given options. Used to
-- define built-in extra sources, such as, C, Cxx, Js, Asm, and Cmm sources.
buildExtraSources
:: String
-- ^ String describing the extra sources being built, for printing.
-> (Verbosity -> LocalBuildInfo -> BuildInfo -> ComponentLocalBuildInfo -> FilePath -> FilePath -> GhcOptions)
-- ^ Function to determine the @'GhcOptions'@ for the
-- invocation of GHC when compiling these extra sources (e.g.
-- @'Internal.componentCxxGhcOptions'@,
-- @'Internal.componentCmmGhcOptions'@)
-> Bool
-- ^ Some types of build sources should not be built in the dynamic way, namely, JS sources.
-- I'm not entirely sure this remains true after we migrate to supporting GHC's JS backend rather than GHCJS.
-- Boolean for "do we allow building these sources the dynamic way?"
-> (Component -> [FilePath])
-- ^ View the extra sources of a component, typically from
-- the build info (e.g. @'asmSources'@, @'cSources'@).
-- @'Executable'@ components might additionally add the
-- program entry point (@main-is@ file) to the extra sources,
-- if it should be compiled as the rest of them.
-> ConfiguredProgram
-- ^ The GHC configured program
-> FilePath
-- ^ The build directory for this target
-> PreBuildComponentInputs
-- ^ The context and component being built in it.
-> IO (NubListR FilePath)
-- ^ Returns the list of extra sources that were built
buildExtraSources description componentSourceGhcOptions wantDyn viewSources ghcProg buildTargetDir =
\PreBuildComponentInputs{buildingWhat, localBuildInfo = lbi, targetInfo} ->
let
bi = componentBuildInfo (targetComponent targetInfo)
verbosity = buildingWhatVerbosity buildingWhat
clbi = targetCLBI targetInfo
sources = viewSources (targetComponent targetInfo)
comp = compiler lbi
platform = hostPlatform lbi
-- Instead of keeping this logic here, we really just want to
-- receive as an input the `neededWays` from GHC/Build.build and build
-- accordingly, since we've already determined the extra needed ways
-- needed for e.g. template haskell. Although we'd have to account for 'wantDyn'.
isGhcDynamic = isDynamic comp
doingTH = usesTemplateHaskellOrQQ bi
forceSharedLib = doingTH && isGhcDynamic
runGhcProg = runGHC verbosity ghcProg comp platform
buildAction sourceFile = do
let baseSrcOpts =
componentSourceGhcOptions
verbosity
lbi
bi
clbi
buildTargetDir
sourceFile
vanillaSrcOpts
-- Dynamic GHC requires C sources to be built
-- with -fPIC for REPL to work. See #2207.
| isGhcDynamic && wantDyn = baseSrcOpts{ghcOptFPic = toFlag True}
| otherwise = baseSrcOpts
profSrcOpts =
vanillaSrcOpts
`mappend` mempty
{ ghcOptProfilingMode = toFlag True
}
sharedSrcOpts =
vanillaSrcOpts
`mappend` mempty
{ ghcOptFPic = toFlag True
, ghcOptDynLinkMode = toFlag GhcDynamicOnly
}
-- TODO: Placing all Haskell, C, & C++ objects in a single directory
-- Has the potential for file collisions. In general we would
-- consider this a user error. However, we should strive to
-- add a warning if this occurs.
odir = fromFlag (ghcOptObjDir vanillaSrcOpts)
compileIfNeeded opts = do
needsRecomp <- checkNeedsRecompilation sourceFile opts
when needsRecomp $ runGhcProg opts
-- TODO: This whole section can be streamlined to the
-- wantedWays+neededWays logic used in Build/Modules.hs
createDirectoryIfMissingVerbose verbosity True odir
case targetComponent targetInfo of
-- For libraries, we compile extra objects in the three ways: vanilla, shared, and profiled.
-- We suffix shared objects with .dyn_o and profiled ones with .p_o.
CLib _lib
-- Unless for repl, in which case we only need the vanilla way
| BuildRepl _ <- buildingWhat ->
compileIfNeeded vanillaSrcOpts
| otherwise ->
do
compileIfNeeded vanillaSrcOpts
when (wantDyn && (forceSharedLib || withSharedLib lbi)) $
compileIfNeeded sharedSrcOpts{ghcOptObjSuffix = toFlag "dyn_o"}
when (withProfLib lbi) $
compileIfNeeded profSrcOpts{ghcOptObjSuffix = toFlag "p_o"}
-- For foreign libraries, we determine with which options to build the
-- objects (vanilla vs shared vs profiled)
CFLib flib
| withProfExe lbi -> -- It doesn't sound right to query "ProfExe" for a foreign library...
compileIfNeeded profSrcOpts
| withDynFLib flib && wantDyn ->
compileIfNeeded sharedSrcOpts
| otherwise ->
compileIfNeeded vanillaSrcOpts
-- For the remaining component types (Exec, Test, Bench), we also
-- determine with which options to build the objects (vanilla vs shared vs
-- profiled), but predicate is the same for the three kinds.
_exeLike
| withProfExe lbi ->
compileIfNeeded profSrcOpts
| withDynExe lbi && wantDyn ->
compileIfNeeded sharedSrcOpts
| otherwise ->
compileIfNeeded vanillaSrcOpts
in
-- build any sources
if (null sources || componentIsIndefinite clbi)
then return mempty
else do
info verbosity ("Building " ++ description ++ "...")
traverse_ buildAction sources
return (toNubListR sources)