{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TupleSections #-}

module Distribution.Simple.GHC.Build.Modules (buildHaskellModules, BuildWay (..), buildWayPrefix) where

import Control.Monad.IO.Class
import Distribution.Compat.Prelude

import Data.List (sortOn, (\\))
import qualified Data.Set as Set
import Distribution.CabalSpecVersion
import Distribution.ModuleName (ModuleName)
import qualified Distribution.PackageDescription as PD
import Distribution.Pretty
import Distribution.Simple.Build.Inputs
import Distribution.Simple.Compiler
import Distribution.Simple.GHC.Build.Utils
import qualified Distribution.Simple.GHC.Internal as Internal
import qualified Distribution.Simple.Hpc as Hpc
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.Program.GHC
import Distribution.Simple.Program.Types
import Distribution.Simple.Setup.Common
import Distribution.Simple.Utils
import Distribution.Types.Benchmark
import Distribution.Types.BenchmarkInterface
import Distribution.Types.BuildInfo
import Distribution.Types.Executable
import Distribution.Types.ForeignLib
import Distribution.Types.PackageName.Magic
import Distribution.Types.ParStrat
import Distribution.Types.TestSuite
import Distribution.Types.TestSuiteInterface
import Distribution.Utils.NubList
import System.FilePath

{-
Note [Building Haskell Modules accounting for TH]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

There are multiple ways in which we may want to build our Haskell modules:
  * The static way (-static)
  * The dynamic/shared way (-dynamic)
  * The profiled way (-prof)

For libraries, we may /want/ to build modules in all three ways, or in any combination, depending on user options.
For executables, we just /want/ to build the executable in the requested way.

In practice, however, we may /need/ to build modules in additional ways beyonds the ones that were requested.
This can happen because of Template Haskell.

When we're using Template Haskell, we /need/ to additionally build modules with
the used GHC's default/vanilla ABI. This is because the code that TH needs to
run at compile time needs to be the vanilla ABI so it can be loaded up and run
by the compiler. With dynamic-by-default GHC the TH object files loaded at
compile-time need to be .dyn_o instead of .o.

  * If the GHC is dynamic by default, that means we may need to also build
  the dynamic way in addition the wanted way.

  * If the GHC is static by default, we may need to build statically additionally.

Of course, if the /wanted/ way is the way additionally /needed/ for TH, we don't need to do extra work.

If it turns out that in the end we need to build both statically and
dynamically, we want to make use of GHC's -static -dynamic-too capability, which
builds modules in the two ways in a single invocation.

If --dynamic-too is not supported by the GHC, then we need to be careful about
the order in which modules are built. Specifically, we must first build the
modules for TH with the vanilla ABI, and only afterwards the desired
(non-default) ways.

A few examples:

To build an executable with profiling, with a dynamic by default GHC, and TH is used:
  * Build dynamic (needed) objects
  * Build profiled objects

To build a library with profiling and dynamically, with a static by default GHC, and TH is used:
  * Build dynamic (wanted) and static (needed) objects together with --dynamic-too
  * Build profiled objects

To build an executable statically, with a static by default GHC, regardless of whether TH is used:
  * Simply build static objects

-}

-- | Compile the Haskell modules of the component being built.
buildHaskellModules
  :: Flag ParStrat
  -- ^ The parallelism strategy (e.g. num of jobs)
  -> ConfiguredProgram
  -- ^ The GHC configured program
  -> PD.PackageDescription
  -- ^ The package description
  -> FilePath
  -- ^ The path to the build directory for this target, which
  -- has already been created.
  -> Set.Set BuildWay
  -- ^ The set of wanted build ways according to user options
  -> PreBuildComponentInputs
  -- ^ The context and component being built in it.
  -> IO (BuildWay -> GhcOptions)
  -- ^ Returns a mapping from build ways to the 'GhcOptions' used in the
  -- invocation used to compile the component in that 'BuildWay'.
  -- This can be useful in, eg, a linker invocation, in which we want to use the
  -- same options and list the same inputs as those used for building.
buildHaskellModules :: Flag ParStrat
-> ConfiguredProgram
-> PackageDescription
-> FilePath
-> Set BuildWay
-> PreBuildComponentInputs
-> IO (BuildWay -> GhcOptions)
buildHaskellModules Flag ParStrat
numJobs ConfiguredProgram
ghcProg PackageDescription
pkg_descr FilePath
buildTargetDir Set BuildWay
wantedWays PreBuildComponentInputs
pbci = do
  -- See Note [Building Haskell Modules accounting for TH]

  let
    verbosity :: Verbosity
verbosity = PreBuildComponentInputs -> Verbosity
buildVerbosity PreBuildComponentInputs
pbci
    isLib :: Bool
isLib = PreBuildComponentInputs -> Bool
buildIsLib PreBuildComponentInputs
pbci
    clbi :: ComponentLocalBuildInfo
clbi = PreBuildComponentInputs -> ComponentLocalBuildInfo
buildCLBI PreBuildComponentInputs
pbci
    lbi :: LocalBuildInfo
lbi = PreBuildComponentInputs -> LocalBuildInfo
localBuildInfo PreBuildComponentInputs
pbci
    bi :: BuildInfo
bi = PreBuildComponentInputs -> BuildInfo
buildBI PreBuildComponentInputs
pbci
    what :: BuildingWhat
what = PreBuildComponentInputs -> BuildingWhat
buildingWhat PreBuildComponentInputs
pbci
    comp :: Compiler
comp = PreBuildComponentInputs -> Compiler
buildCompiler PreBuildComponentInputs
pbci

    -- If this component will be loaded into a repl, we don't compile the modules at all.
    forRepl :: Bool
forRepl
      | BuildRepl{} <- BuildingWhat
what = Bool
True
      | Bool
otherwise = Bool
False

  -- TODO: do we need to put hs-boot files into place for mutually recursive
  -- modules?  FIX: what about exeName.hi-boot?

  -- Determine if program coverage should be enabled and if so, what
  -- '-hpcdir' should be.
  let isCoverageEnabled :: Bool
isCoverageEnabled = if Bool
isLib then LocalBuildInfo -> Bool
libCoverage LocalBuildInfo
lbi else LocalBuildInfo -> Bool
exeCoverage LocalBuildInfo
lbi
      hpcdir :: Way -> Flag FilePath
hpcdir Way
way
        | Bool
forRepl = Flag FilePath
forall a. Monoid a => a
mempty -- HPC is not supported in ghci
        | Bool
isCoverageEnabled = FilePath -> Flag FilePath
forall a. a -> Flag a
Flag (FilePath -> Flag FilePath) -> FilePath -> Flag FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> Way -> FilePath
Hpc.mixDir (FilePath
buildTargetDir FilePath -> FilePath -> FilePath
</> FilePath
extraCompilationArtifacts) Way
way
        | Bool
otherwise = Flag FilePath
forall a. Monoid a => a
mempty

  ([FilePath]
inputFiles, [ModuleName]
inputModules) <- FilePath
-> PackageDescription
-> PreBuildComponentInputs
-> IO ([FilePath], [ModuleName])
componentInputs FilePath
buildTargetDir PackageDescription
pkg_descr PreBuildComponentInputs
pbci

  let
    runGhcProg :: GhcOptions -> IO ()
runGhcProg = Verbosity
-> ConfiguredProgram -> Compiler -> Platform -> GhcOptions -> IO ()
runGHC Verbosity
verbosity ConfiguredProgram
ghcProg Compiler
comp Platform
platform
    platform :: Platform
platform = LocalBuildInfo -> Platform
hostPlatform LocalBuildInfo
lbi

    -- See Note [Building Haskell Modules accounting for TH]
    doingTH :: Bool
doingTH = BuildInfo -> Bool
usesTemplateHaskellOrQQ BuildInfo
bi

    -- We define the base opts which are shared across different build ways in
    -- 'buildHaskellModules'
    baseOpts :: BuildWay -> GhcOptions
baseOpts BuildWay
way =
      (Verbosity
-> LocalBuildInfo
-> BuildInfo
-> ComponentLocalBuildInfo
-> FilePath
-> GhcOptions
Internal.componentGhcOptions Verbosity
verbosity LocalBuildInfo
lbi BuildInfo
bi ComponentLocalBuildInfo
clbi FilePath
buildTargetDir)
        GhcOptions -> GhcOptions -> GhcOptions
forall a. Monoid a => a -> a -> a
`mappend` GhcOptions
forall a. Monoid a => a
mempty
          { ghcOptMode = toFlag GhcModeMake
          , -- Previously we didn't pass -no-link when building libs,
            -- but I think that could result in a bug (e.g. if a lib module is
            -- called Main and exports main). So we really want nolink when
            -- building libs too (TODO).
            ghcOptNoLink = if isLib then NoFlag else toFlag True
          , ghcOptNumJobs = numJobs
          , ghcOptInputModules = toNubListR inputModules
          , ghcOptInputFiles =
              toNubListR $
                if PD.package pkg_descr == fakePackageId
                  then filter isHaskell inputFiles
                  else inputFiles
          , ghcOptInputScripts =
              toNubListR $
                if PD.package pkg_descr == fakePackageId
                  then filter (not . isHaskell) inputFiles
                  else []
          , ghcOptExtra = buildWayExtraHcOptions way GHC bi
          , ghcOptHiSuffix = optSuffixFlag (buildWayPrefix way) "hi"
          , ghcOptObjSuffix = optSuffixFlag (buildWayPrefix way) "o"
          , ghcOptHPCDir = hpcdir (buildWayHpcWay way) -- maybe this should not be passed for vanilla?
          }
      where
        optSuffixFlag :: FilePath -> FilePath -> Flag FilePath
optSuffixFlag FilePath
"" FilePath
_ = Flag FilePath
forall a. Flag a
NoFlag
        optSuffixFlag FilePath
pre FilePath
x = FilePath -> Flag FilePath
forall a. a -> Flag a
toFlag (FilePath
pre FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
x)

    -- For libs we don't pass -static when building static, leaving it
    -- implicit. We should just always pass -static, but we don't want to
    -- change behaviour when doing the refactor.
    staticOpts :: GhcOptions
staticOpts = (BuildWay -> GhcOptions
baseOpts BuildWay
StaticWay){ghcOptDynLinkMode = if isLib then NoFlag else toFlag GhcStaticOnly}
    dynOpts :: GhcOptions
dynOpts =
      (BuildWay -> GhcOptions
baseOpts BuildWay
DynWay)
        { ghcOptDynLinkMode = toFlag GhcDynamicOnly -- use -dynamic
        , -- TODO: Does it hurt to set -fPIC for executables?
          ghcOptFPic = toFlag True -- use -fPIC
        }
    profOpts :: GhcOptions
profOpts =
      (BuildWay -> GhcOptions
baseOpts BuildWay
ProfWay)
        { ghcOptProfilingMode = toFlag True
        , ghcOptProfilingAuto =
            Internal.profDetailLevelFlag
              (if isLib then True else False)
              ((if isLib then withProfLibDetail else withProfExeDetail) lbi)
        }
    -- Options for building both static and dynamic way at the same time, using
    -- the GHC flag -static and -dynamic-too
    dynTooOpts :: GhcOptions
dynTooOpts =
      (BuildWay -> GhcOptions
baseOpts BuildWay
StaticWay)
        { ghcOptDynLinkMode = toFlag GhcStaticAndDynamic -- use -dynamic-too
        , ghcOptDynHiSuffix = toFlag (buildWayPrefix DynWay ++ "hi")
        , ghcOptDynObjSuffix = toFlag (buildWayPrefix DynWay ++ "o")
        , ghcOptHPCDir = hpcdir Hpc.Dyn
        -- Should we pass hcSharedOpts in the -dynamic-too ghc invocation?
        -- (Note that `baseOtps StaticWay = hcStaticOptions`, not hcSharedOpts)
        }

    -- Determines how to build for each way, also serves as the base options
    -- for loading modules in 'linkOrLoadComponent'
    buildOpts :: BuildWay -> GhcOptions
buildOpts BuildWay
way = case BuildWay
way of
      BuildWay
StaticWay -> GhcOptions
staticOpts
      BuildWay
DynWay -> GhcOptions
dynOpts
      BuildWay
ProfWay -> GhcOptions
profOpts

    defaultGhcWay :: BuildWay
defaultGhcWay = if Compiler -> Bool
isDynamic Compiler
comp then BuildWay
DynWay else BuildWay
StaticWay

  -- If there aren't modules, or if we're loading the modules in repl, don't build.
  Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Bool
forRepl Bool -> Bool -> Bool
|| ([FilePath] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [FilePath]
inputFiles Bool -> Bool -> Bool
&& [ModuleName] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [ModuleName]
inputModules)) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ IO () -> IO ()
forall a. IO a -> IO a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
    -- See Note [Building Haskell Modules accounting for TH]
    let
      neededWays :: Set BuildWay
neededWays =
        Set BuildWay
wantedWays
          Set BuildWay -> Set BuildWay -> Set BuildWay
forall a. Semigroup a => a -> a -> a
<> [BuildWay] -> Set BuildWay
forall a. Ord a => [a] -> Set a
Set.fromList
            -- TODO: You also don't need to build the GHC way when doing TH if
            -- you are using an external interpreter!!
            [BuildWay
defaultGhcWay | Bool
doingTH Bool -> Bool -> Bool
&& BuildWay
defaultGhcWay BuildWay -> Set BuildWay -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.notMember` Set BuildWay
wantedWays]

      -- If we need both static and dynamic, use dynamic-too instead of
      -- compiling twice (if we support it)
      useDynamicToo :: Bool
useDynamicToo =
        BuildWay
StaticWay BuildWay -> Set BuildWay -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member` Set BuildWay
neededWays
          Bool -> Bool -> Bool
&& BuildWay
DynWay BuildWay -> Set BuildWay -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.member` Set BuildWay
neededWays
          Bool -> Bool -> Bool
&& Compiler -> Bool
supportsDynamicToo Compiler
comp
          Bool -> Bool -> Bool
&& [FilePath] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (CompilerFlavor -> BuildInfo -> [FilePath]
hcSharedOptions CompilerFlavor
GHC BuildInfo
bi)

      -- The ways we'll build, in order
      orderedBuilds :: [IO ()]
orderedBuilds
        -- If we can use dynamic-too, do it first. The default GHC way can only
        -- be static or dynamic, so, if we build both right away, any modules
        -- possibly needed by TH later (e.g. if building profiled) are already built.
        | Bool
useDynamicToo =
            [IO ()
buildStaticAndDynamicToo]
              [IO ()] -> [IO ()] -> [IO ()]
forall a. [a] -> [a] -> [a]
++ (GhcOptions -> IO ()
runGhcProg (GhcOptions -> IO ())
-> (BuildWay -> GhcOptions) -> BuildWay -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BuildWay -> GhcOptions
buildOpts (BuildWay -> IO ()) -> [BuildWay] -> [IO ()]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Set BuildWay -> [BuildWay]
forall a. Set a -> [a]
Set.toList Set BuildWay
neededWays [BuildWay] -> [BuildWay] -> [BuildWay]
forall a. Eq a => [a] -> [a] -> [a]
\\ [BuildWay
StaticWay, BuildWay
DynWay])
        -- Otherwise, we need to ensure the defaultGhcWay is built first
        | Bool
otherwise =
            GhcOptions -> IO ()
runGhcProg (GhcOptions -> IO ())
-> (BuildWay -> GhcOptions) -> BuildWay -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. BuildWay -> GhcOptions
buildOpts (BuildWay -> IO ()) -> [BuildWay] -> [IO ()]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (BuildWay -> Int) -> [BuildWay] -> [BuildWay]
forall b a. Ord b => (a -> b) -> [a] -> [a]
sortOn (\BuildWay
w -> if BuildWay
w BuildWay -> BuildWay -> Bool
forall a. Eq a => a -> a -> Bool
== BuildWay
defaultGhcWay then Int
0 else BuildWay -> Int
forall a. Enum a => a -> Int
fromEnum BuildWay
w Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Set BuildWay -> [BuildWay]
forall a. Set a -> [a]
Set.toList Set BuildWay
neededWays)

      buildStaticAndDynamicToo :: IO ()
buildStaticAndDynamicToo = do
        GhcOptions -> IO ()
runGhcProg GhcOptions
dynTooOpts
        case (Way -> Flag FilePath
hpcdir Way
Hpc.Dyn, Way -> Flag FilePath
hpcdir Way
Hpc.Vanilla) of
          (Flag FilePath
dynDir, Flag FilePath
vanillaDir) ->
            -- When the vanilla and shared library builds are done
            -- in one pass, only one set of HPC module interfaces
            -- are generated. This set should suffice for both
            -- static and dynamically linked executables. We copy
            -- the modules interfaces so they are available under
            -- both ways.
            Verbosity -> FilePath -> FilePath -> IO ()
copyDirectoryRecursive Verbosity
verbosity FilePath
dynDir FilePath
vanillaDir
          (Flag FilePath, Flag FilePath)
_ -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
     in
      -- REVIEW:ADD? info verbosity "Building Haskell Sources..."
      [IO ()] -> IO ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ [IO ()]
orderedBuilds
  (BuildWay -> GhcOptions) -> IO (BuildWay -> GhcOptions)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return BuildWay -> GhcOptions
buildOpts

data BuildWay = StaticWay | DynWay | ProfWay
  deriving (BuildWay -> BuildWay -> Bool
(BuildWay -> BuildWay -> Bool)
-> (BuildWay -> BuildWay -> Bool) -> Eq BuildWay
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: BuildWay -> BuildWay -> Bool
== :: BuildWay -> BuildWay -> Bool
$c/= :: BuildWay -> BuildWay -> Bool
/= :: BuildWay -> BuildWay -> Bool
Eq, Eq BuildWay
Eq BuildWay =>
(BuildWay -> BuildWay -> Ordering)
-> (BuildWay -> BuildWay -> Bool)
-> (BuildWay -> BuildWay -> Bool)
-> (BuildWay -> BuildWay -> Bool)
-> (BuildWay -> BuildWay -> Bool)
-> (BuildWay -> BuildWay -> BuildWay)
-> (BuildWay -> BuildWay -> BuildWay)
-> Ord BuildWay
BuildWay -> BuildWay -> Bool
BuildWay -> BuildWay -> Ordering
BuildWay -> BuildWay -> BuildWay
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: BuildWay -> BuildWay -> Ordering
compare :: BuildWay -> BuildWay -> Ordering
$c< :: BuildWay -> BuildWay -> Bool
< :: BuildWay -> BuildWay -> Bool
$c<= :: BuildWay -> BuildWay -> Bool
<= :: BuildWay -> BuildWay -> Bool
$c> :: BuildWay -> BuildWay -> Bool
> :: BuildWay -> BuildWay -> Bool
$c>= :: BuildWay -> BuildWay -> Bool
>= :: BuildWay -> BuildWay -> Bool
$cmax :: BuildWay -> BuildWay -> BuildWay
max :: BuildWay -> BuildWay -> BuildWay
$cmin :: BuildWay -> BuildWay -> BuildWay
min :: BuildWay -> BuildWay -> BuildWay
Ord, Int -> BuildWay -> FilePath -> FilePath
[BuildWay] -> FilePath -> FilePath
BuildWay -> FilePath
(Int -> BuildWay -> FilePath -> FilePath)
-> (BuildWay -> FilePath)
-> ([BuildWay] -> FilePath -> FilePath)
-> Show BuildWay
forall a.
(Int -> a -> FilePath -> FilePath)
-> (a -> FilePath) -> ([a] -> FilePath -> FilePath) -> Show a
$cshowsPrec :: Int -> BuildWay -> FilePath -> FilePath
showsPrec :: Int -> BuildWay -> FilePath -> FilePath
$cshow :: BuildWay -> FilePath
show :: BuildWay -> FilePath
$cshowList :: [BuildWay] -> FilePath -> FilePath
showList :: [BuildWay] -> FilePath -> FilePath
Show, Int -> BuildWay
BuildWay -> Int
BuildWay -> [BuildWay]
BuildWay -> BuildWay
BuildWay -> BuildWay -> [BuildWay]
BuildWay -> BuildWay -> BuildWay -> [BuildWay]
(BuildWay -> BuildWay)
-> (BuildWay -> BuildWay)
-> (Int -> BuildWay)
-> (BuildWay -> Int)
-> (BuildWay -> [BuildWay])
-> (BuildWay -> BuildWay -> [BuildWay])
-> (BuildWay -> BuildWay -> [BuildWay])
-> (BuildWay -> BuildWay -> BuildWay -> [BuildWay])
-> Enum BuildWay
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
$csucc :: BuildWay -> BuildWay
succ :: BuildWay -> BuildWay
$cpred :: BuildWay -> BuildWay
pred :: BuildWay -> BuildWay
$ctoEnum :: Int -> BuildWay
toEnum :: Int -> BuildWay
$cfromEnum :: BuildWay -> Int
fromEnum :: BuildWay -> Int
$cenumFrom :: BuildWay -> [BuildWay]
enumFrom :: BuildWay -> [BuildWay]
$cenumFromThen :: BuildWay -> BuildWay -> [BuildWay]
enumFromThen :: BuildWay -> BuildWay -> [BuildWay]
$cenumFromTo :: BuildWay -> BuildWay -> [BuildWay]
enumFromTo :: BuildWay -> BuildWay -> [BuildWay]
$cenumFromThenTo :: BuildWay -> BuildWay -> BuildWay -> [BuildWay]
enumFromThenTo :: BuildWay -> BuildWay -> BuildWay -> [BuildWay]
Enum)

-- | Returns the object/interface extension prefix for the given build way (e.g. "dyn_" for 'DynWay')
buildWayPrefix :: BuildWay -> String
buildWayPrefix :: BuildWay -> FilePath
buildWayPrefix = \case
  BuildWay
StaticWay -> FilePath
""
  BuildWay
ProfWay -> FilePath
"p_"
  BuildWay
DynWay -> FilePath
"dyn_"

-- | Returns the corresponding 'Hpc.Way' for a 'BuildWay'
buildWayHpcWay :: BuildWay -> Hpc.Way
buildWayHpcWay :: BuildWay -> Way
buildWayHpcWay = \case
  BuildWay
StaticWay -> Way
Hpc.Vanilla
  BuildWay
ProfWay -> Way
Hpc.Prof
  BuildWay
DynWay -> Way
Hpc.Dyn

-- | Returns a function to extract the extra haskell compiler options from a
-- 'BuildInfo' and 'CompilerFlavor'
buildWayExtraHcOptions :: BuildWay -> CompilerFlavor -> BuildInfo -> [String]
buildWayExtraHcOptions :: BuildWay -> CompilerFlavor -> BuildInfo -> [FilePath]
buildWayExtraHcOptions = \case
  BuildWay
StaticWay -> CompilerFlavor -> BuildInfo -> [FilePath]
hcStaticOptions
  BuildWay
ProfWay -> CompilerFlavor -> BuildInfo -> [FilePath]
hcProfOptions
  BuildWay
DynWay -> CompilerFlavor -> BuildInfo -> [FilePath]
hcSharedOptions

-- | Returns a pair of the Haskell input files and Haskell modules of the
-- component being built.
--
-- The "input files" are either the path to the main Haskell module, or a repl
-- script (that does not necessarily have an extension).
componentInputs
  :: FilePath
  -- ^ Target build dir
  -> PD.PackageDescription
  -> PreBuildComponentInputs
  -- ^ The context and component being built in it.
  -> IO ([FilePath], [ModuleName])
  -- ^ The Haskell input files, and the Haskell modules
componentInputs :: FilePath
-> PackageDescription
-> PreBuildComponentInputs
-> IO ([FilePath], [ModuleName])
componentInputs FilePath
buildTargetDir PackageDescription
pkg_descr PreBuildComponentInputs
pbci = do
  let
    verbosity :: Verbosity
verbosity = PreBuildComponentInputs -> Verbosity
buildVerbosity PreBuildComponentInputs
pbci
    component :: Component
component = PreBuildComponentInputs -> Component
buildComponent PreBuildComponentInputs
pbci
    clbi :: ComponentLocalBuildInfo
clbi = PreBuildComponentInputs -> ComponentLocalBuildInfo
buildCLBI PreBuildComponentInputs
pbci

  case Component
component of
    CLib Library
lib ->
      ([FilePath], [ModuleName]) -> IO ([FilePath], [ModuleName])
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([], Library -> ComponentLocalBuildInfo -> [ModuleName]
allLibModules Library
lib ComponentLocalBuildInfo
clbi)
    CFLib ForeignLib
flib ->
      ([FilePath], [ModuleName]) -> IO ([FilePath], [ModuleName])
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([], ForeignLib -> [ModuleName]
foreignLibModules ForeignLib
flib)
    CExe Executable{buildInfo :: Executable -> BuildInfo
buildInfo = BuildInfo
bi', FilePath
modulePath :: FilePath
modulePath :: Executable -> FilePath
modulePath} ->
      Verbosity -> BuildInfo -> FilePath -> IO ([FilePath], [ModuleName])
forall {m :: * -> *}.
MonadIO m =>
Verbosity -> BuildInfo -> FilePath -> m ([FilePath], [ModuleName])
exeLikeInputs Verbosity
verbosity BuildInfo
bi' FilePath
modulePath
    CTest TestSuite{testBuildInfo :: TestSuite -> BuildInfo
testBuildInfo = BuildInfo
bi', testInterface :: TestSuite -> TestSuiteInterface
testInterface = TestSuiteExeV10 Version
_ FilePath
mainFile} ->
      Verbosity -> BuildInfo -> FilePath -> IO ([FilePath], [ModuleName])
forall {m :: * -> *}.
MonadIO m =>
Verbosity -> BuildInfo -> FilePath -> m ([FilePath], [ModuleName])
exeLikeInputs Verbosity
verbosity BuildInfo
bi' FilePath
mainFile
    CBench Benchmark{benchmarkBuildInfo :: Benchmark -> BuildInfo
benchmarkBuildInfo = BuildInfo
bi', benchmarkInterface :: Benchmark -> BenchmarkInterface
benchmarkInterface = BenchmarkExeV10 Version
_ FilePath
mainFile} ->
      Verbosity -> BuildInfo -> FilePath -> IO ([FilePath], [ModuleName])
forall {m :: * -> *}.
MonadIO m =>
Verbosity -> BuildInfo -> FilePath -> m ([FilePath], [ModuleName])
exeLikeInputs Verbosity
verbosity BuildInfo
bi' FilePath
mainFile
    CTest TestSuite{} -> FilePath -> IO ([FilePath], [ModuleName])
forall a. HasCallStack => FilePath -> a
error FilePath
"testSuiteExeV10AsExe: wrong kind"
    CBench Benchmark{} -> FilePath -> IO ([FilePath], [ModuleName])
forall a. HasCallStack => FilePath -> a
error FilePath
"benchmarkExeV10asExe: wrong kind"
  where
    exeLikeInputs :: Verbosity -> BuildInfo -> FilePath -> m ([FilePath], [ModuleName])
exeLikeInputs Verbosity
verbosity BuildInfo
bnfo FilePath
modulePath = IO ([FilePath], [ModuleName]) -> m ([FilePath], [ModuleName])
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO ([FilePath], [ModuleName]) -> m ([FilePath], [ModuleName]))
-> IO ([FilePath], [ModuleName]) -> m ([FilePath], [ModuleName])
forall a b. (a -> b) -> a -> b
$ do
      FilePath
main <- Verbosity -> FilePath -> (BuildInfo, FilePath) -> IO FilePath
findExecutableMain Verbosity
verbosity FilePath
buildTargetDir (BuildInfo
bnfo, FilePath
modulePath)
      let mainModName :: ModuleName
mainModName = BuildInfo -> ModuleName
exeMainModuleName BuildInfo
bnfo
          otherModNames :: [ModuleName]
otherModNames = BuildInfo -> [ModuleName]
otherModules BuildInfo
bnfo

      -- Scripts have fakePackageId and are always Haskell but can have any extension.
      if FilePath -> Bool
isHaskell FilePath
main Bool -> Bool -> Bool
|| PackageDescription -> PackageIdentifier
PD.package PackageDescription
pkg_descr PackageIdentifier -> PackageIdentifier -> Bool
forall a. Eq a => a -> a -> Bool
== PackageIdentifier
fakePackageId
        then
          if PackageDescription -> CabalSpecVersion
PD.specVersion PackageDescription
pkg_descr CabalSpecVersion -> CabalSpecVersion -> Bool
forall a. Ord a => a -> a -> Bool
< CabalSpecVersion
CabalSpecV2_0 Bool -> Bool -> Bool
&& (ModuleName
mainModName ModuleName -> [ModuleName] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ModuleName]
otherModNames)
            then do
              -- The cabal manual clearly states that `other-modules` is
              -- intended for non-main modules.  However, there's at least one
              -- important package on Hackage (happy-1.19.5) which
              -- violates this. We workaround this here so that we don't
              -- invoke GHC with e.g.  'ghc --make Main src/Main.hs' which
              -- would result in GHC complaining about duplicate Main
              -- modules.
              --
              -- Finally, we only enable this workaround for
              -- specVersion < 2, as 'cabal-version:>=2.0' cabal files
              -- have no excuse anymore to keep doing it wrong... ;-)
              Verbosity -> FilePath -> IO ()
warn Verbosity
verbosity (FilePath -> IO ()) -> FilePath -> IO ()
forall a b. (a -> b) -> a -> b
$
                FilePath
"Enabling workaround for Main module '"
                  FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ ModuleName -> FilePath
forall a. Pretty a => a -> FilePath
prettyShow ModuleName
mainModName
                  FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
"' listed in 'other-modules' illegally!"
              ([FilePath], [ModuleName]) -> IO ([FilePath], [ModuleName])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([FilePath
main], (ModuleName -> Bool) -> [ModuleName] -> [ModuleName]
forall a. (a -> Bool) -> [a] -> [a]
filter (ModuleName -> ModuleName -> Bool
forall a. Eq a => a -> a -> Bool
/= ModuleName
mainModName) [ModuleName]
otherModNames)
            else ([FilePath], [ModuleName]) -> IO ([FilePath], [ModuleName])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([FilePath
main], [ModuleName]
otherModNames)
        else ([FilePath], [ModuleName]) -> IO ([FilePath], [ModuleName])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([], [ModuleName]
otherModNames)