{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}

-----------------------------------------------------------------------------

-- |
-- Module      :  Distribution.Simple.GHC.Internal
-- Copyright   :  Isaac Jones 2003-2007
--
-- Maintainer  :  cabal-devel@haskell.org
-- Portability :  portable
--
-- This module contains functions shared by GHC (Distribution.Simple.GHC)
-- and GHC-derived compilers.
module Distribution.Simple.GHC.Internal
  ( configureToolchain
  , getLanguages
  , getExtensions
  , targetPlatform
  , getGhcInfo
  , componentCcGhcOptions
  , componentCmmGhcOptions
  , componentCxxGhcOptions
  , componentAsmGhcOptions
  , componentJsGhcOptions
  , componentGhcOptions
  , mkGHCiLibName
  , mkGHCiProfLibName
  , filterGhciFlags
  , ghcLookupProperty
  , getHaskellObjects
  , mkGhcOptPackages
  , substTopDir
  , checkPackageDbEnvVar
  , profDetailLevelFlag

    -- * GHC platform and version strings
  , ghcArchString
  , ghcOsString
  , ghcPlatformAndVersionString

    -- * Constructing GHC environment files
  , GhcEnvironmentFileEntry (..)
  , writeGhcEnvironmentFile
  , simpleGhcEnvironmentFile
  , ghcEnvironmentFileName
  , renderGhcEnvironmentFile
  , renderGhcEnvironmentFileEntry
  ) where

import Distribution.Compat.Prelude
import Prelude ()

import Distribution.Backpack
import Distribution.Compat.Stack
import qualified Distribution.InstalledPackageInfo as IPI
import Distribution.Lex
import qualified Distribution.ModuleName as ModuleName
import Distribution.PackageDescription
import Distribution.Parsec (simpleParsec)
import Distribution.Pretty (prettyShow)
import Distribution.Simple.BuildPaths
import Distribution.Simple.Compiler
import Distribution.Simple.Flag (Flag, maybeToFlag, toFlag)
import Distribution.Simple.GHC.ImplInfo
import Distribution.Simple.LocalBuildInfo
import Distribution.Simple.Program
import Distribution.Simple.Program.GHC
import Distribution.Simple.Utils
import Distribution.System
import Distribution.Types.ComponentLocalBuildInfo
import Distribution.Types.LocalBuildInfo
import Distribution.Types.TargetInfo
import Distribution.Types.UnitId
import Distribution.Utils.NubList (toNubListR)
import Distribution.Utils.Path
import Distribution.Verbosity
import Distribution.Version (Version)
import Language.Haskell.Extension

import qualified Data.ByteString.Lazy.Char8 as BS
import qualified Data.Map as Map
import qualified Data.Set as Set
import Distribution.Types.ComponentId (ComponentId)
import System.Directory (getDirectoryContents, getTemporaryDirectory)
import System.Environment (getEnv)
import System.FilePath
  ( takeDirectory
  , takeExtension
  , takeFileName
  , (<.>)
  , (</>)
  )
import System.IO (hClose, hPutStrLn)

targetPlatform :: [(String, String)] -> Maybe Platform
targetPlatform :: [(String, String)] -> Maybe Platform
targetPlatform [(String, String)]
ghcInfo = String -> Maybe Platform
platformFromTriple (String -> Maybe Platform) -> Maybe String -> Maybe Platform
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< String -> [(String, String)] -> Maybe String
forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
"Target platform" [(String, String)]
ghcInfo

-- | Adjust the way we find and configure gcc and ld
configureToolchain
  :: GhcImplInfo
  -> ConfiguredProgram
  -> Map String String
  -> ProgramDb
  -> ProgramDb
configureToolchain :: GhcImplInfo
-> ConfiguredProgram -> Map String String -> ProgramDb -> ProgramDb
configureToolchain GhcImplInfo
_implInfo ConfiguredProgram
ghcProg Map String String
ghcInfo =
  Program -> ProgramDb -> ProgramDb
addKnownProgram
    Program
gccProgram
      { programFindLocation = findProg gccProgramName extraGccPath
      , programPostConf = configureGcc
      }
    (ProgramDb -> ProgramDb)
-> (ProgramDb -> ProgramDb) -> ProgramDb -> ProgramDb
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Program -> ProgramDb -> ProgramDb
addKnownProgram
      Program
ldProgram
        { programFindLocation = findProg ldProgramName extraLdPath
        , programPostConf = configureLd
        }
    (ProgramDb -> ProgramDb)
-> (ProgramDb -> ProgramDb) -> ProgramDb -> ProgramDb
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Program -> ProgramDb -> ProgramDb
addKnownProgram
      Program
arProgram
        { programFindLocation = findProg arProgramName extraArPath
        }
    (ProgramDb -> ProgramDb)
-> (ProgramDb -> ProgramDb) -> ProgramDb -> ProgramDb
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Program -> ProgramDb -> ProgramDb
addKnownProgram
      Program
stripProgram
        { programFindLocation = findProg stripProgramName extraStripPath
        }
  where
    compilerDir :: String
compilerDir = String -> String
takeDirectory (ConfiguredProgram -> String
programPath ConfiguredProgram
ghcProg)
    base_dir :: String
base_dir = String -> String
takeDirectory String
compilerDir
    mingwBinDir :: String
mingwBinDir = String
base_dir String -> String -> String
</> String
"mingw" String -> String -> String
</> String
"bin"
    isWindows :: Bool
isWindows = case OS
buildOS of OS
Windows -> Bool
True; OS
_ -> Bool
False
    binPrefix :: String
binPrefix = String
""

    maybeName :: Program -> Maybe FilePath -> String
    maybeName :: Program -> Maybe String -> String
maybeName Program
prog = String -> (String -> String) -> Maybe String -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (Program -> String
programName Program
prog) (String -> String
dropExeExtension (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
takeFileName)

    gccProgramName :: String
gccProgramName = Program -> Maybe String -> String
maybeName Program
gccProgram Maybe String
mbGccLocation
    ldProgramName :: String
ldProgramName = Program -> Maybe String -> String
maybeName Program
ldProgram Maybe String
mbLdLocation
    arProgramName :: String
arProgramName = Program -> Maybe String -> String
maybeName Program
arProgram Maybe String
mbArLocation
    stripProgramName :: String
stripProgramName = Program -> Maybe String -> String
maybeName Program
stripProgram Maybe String
mbStripLocation

    mkExtraPath :: Maybe FilePath -> FilePath -> [FilePath]
    mkExtraPath :: Maybe String -> String -> [String]
mkExtraPath Maybe String
mbPath String
mingwPath
      | Bool
isWindows = [String]
mbDir [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
mingwPath]
      | Bool
otherwise = [String]
mbDir
      where
        mbDir :: [String]
mbDir = Maybe String -> [String]
forall a. Maybe a -> [a]
maybeToList (Maybe String -> [String])
-> (Maybe String -> Maybe String) -> Maybe String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> String) -> Maybe String -> Maybe String
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap String -> String
takeDirectory (Maybe String -> [String]) -> Maybe String -> [String]
forall a b. (a -> b) -> a -> b
$ Maybe String
mbPath

    extraGccPath :: [String]
extraGccPath = Maybe String -> String -> [String]
mkExtraPath Maybe String
mbGccLocation String
windowsExtraGccDir
    extraLdPath :: [String]
extraLdPath = Maybe String -> String -> [String]
mkExtraPath Maybe String
mbLdLocation String
windowsExtraLdDir
    extraArPath :: [String]
extraArPath = Maybe String -> String -> [String]
mkExtraPath Maybe String
mbArLocation String
windowsExtraArDir
    extraStripPath :: [String]
extraStripPath = Maybe String -> String -> [String]
mkExtraPath Maybe String
mbStripLocation String
windowsExtraStripDir

    -- on Windows finding and configuring ghc's gcc & binutils is a bit special
    ( String
windowsExtraGccDir
      , String
windowsExtraLdDir
      , String
windowsExtraArDir
      , String
windowsExtraStripDir
      ) =
        let b :: String
b = String
mingwBinDir String -> String -> String
</> String
binPrefix
         in (String
b, String
b, String
b, String
b)

    findProg
      :: String
      -> [FilePath]
      -> Verbosity
      -> ProgramSearchPath
      -> IO (Maybe (FilePath, [FilePath]))
    findProg :: String
-> [String]
-> Verbosity
-> ProgramSearchPath
-> IO (Maybe (String, [String]))
findProg String
progName [String]
extraPath Verbosity
v ProgramSearchPath
searchpath =
      Verbosity
-> ProgramSearchPath -> String -> IO (Maybe (String, [String]))
findProgramOnSearchPath Verbosity
v ProgramSearchPath
searchpath' String
progName
      where
        searchpath' :: ProgramSearchPath
searchpath' = ((String -> ProgramSearchPathEntry) -> [String] -> ProgramSearchPath
forall a b. (a -> b) -> [a] -> [b]
map String -> ProgramSearchPathEntry
ProgramSearchPathDir [String]
extraPath) ProgramSearchPath -> ProgramSearchPath -> ProgramSearchPath
forall a. [a] -> [a] -> [a]
++ ProgramSearchPath
searchpath

    -- Read tool locations from the 'ghc --info' output. Useful when
    -- cross-compiling.
    mbGccLocation :: Maybe String
mbGccLocation = String -> Map String String -> Maybe String
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
"C compiler command" Map String String
ghcInfo
    mbLdLocation :: Maybe String
mbLdLocation = String -> Map String String -> Maybe String
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
"ld command" Map String String
ghcInfo
    mbArLocation :: Maybe String
mbArLocation = String -> Map String String -> Maybe String
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
"ar command" Map String String
ghcInfo
    mbStripLocation :: Maybe String
mbStripLocation = String -> Map String String -> Maybe String
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
"strip command" Map String String
ghcInfo

    ccFlags :: [String]
ccFlags = String -> [String]
getFlags String
"C compiler flags"
    -- GHC 7.8 renamed "Gcc Linker flags" to "C compiler link flags"
    -- and "Ld Linker flags" to "ld flags" (GHC #4862).
    gccLinkerFlags :: [String]
gccLinkerFlags = String -> [String]
getFlags String
"Gcc Linker flags" [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ String -> [String]
getFlags String
"C compiler link flags"
    ldLinkerFlags :: [String]
ldLinkerFlags = String -> [String]
getFlags String
"Ld Linker flags" [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ String -> [String]
getFlags String
"ld flags"

    -- It appears that GHC 7.6 and earlier encode the tokenized flags as a
    -- [String] in these settings whereas later versions just encode the flags as
    -- String.
    --
    -- We first try to parse as a [String] and if this fails then tokenize the
    -- flags ourself.
    getFlags :: String -> [String]
    getFlags :: String -> [String]
getFlags String
key =
      case String -> Map String String -> Maybe String
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
key Map String String
ghcInfo of
        Maybe String
Nothing -> []
        Just String
flags
          | ([String]
flags', String
"") : [([String], String)]
_ <- ReadS [String]
forall a. Read a => ReadS a
reads String
flags -> [String]
flags'
          | Bool
otherwise -> String -> [String]
tokenizeQuotedWords String
flags

    configureGcc :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram
    configureGcc :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram
configureGcc Verbosity
_v ConfiguredProgram
gccProg = do
      ConfiguredProgram -> IO ConfiguredProgram
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return
        ConfiguredProgram
gccProg
          { programDefaultArgs =
              programDefaultArgs gccProg
                ++ ccFlags
                ++ gccLinkerFlags
          }

    configureLd :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram
    configureLd :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram
configureLd Verbosity
v ConfiguredProgram
ldProg = do
      ConfiguredProgram
ldProg' <- Verbosity -> ConfiguredProgram -> IO ConfiguredProgram
configureLd' Verbosity
v ConfiguredProgram
ldProg
      ConfiguredProgram -> IO ConfiguredProgram
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return
        ConfiguredProgram
ldProg'
          { programDefaultArgs = programDefaultArgs ldProg' ++ ldLinkerFlags
          }

    -- we need to find out if ld supports the -x flag
    configureLd' :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram
    configureLd' :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram
configureLd' Verbosity
verbosity ConfiguredProgram
ldProg = do
      String
tempDir <- IO String
getTemporaryDirectory
      Bool
ldx <- String -> String -> (String -> Handle -> IO Bool) -> IO Bool
forall a. String -> String -> (String -> Handle -> IO a) -> IO a
withTempFile String
tempDir String
".c" ((String -> Handle -> IO Bool) -> IO Bool)
-> (String -> Handle -> IO Bool) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \String
testcfile Handle
testchnd ->
        String -> String -> (String -> Handle -> IO Bool) -> IO Bool
forall a. String -> String -> (String -> Handle -> IO a) -> IO a
withTempFile String
tempDir String
".o" ((String -> Handle -> IO Bool) -> IO Bool)
-> (String -> Handle -> IO Bool) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \String
testofile Handle
testohnd -> do
          Handle -> String -> IO ()
hPutStrLn Handle
testchnd String
"int foo() { return 0; }"
          Handle -> IO ()
hClose Handle
testchnd
          Handle -> IO ()
hClose Handle
testohnd
          Verbosity -> ConfiguredProgram -> [String] -> IO ()
runProgram
            Verbosity
verbosity
            ConfiguredProgram
ghcProg
            [ String
"-hide-all-packages"
            , String
"-c"
            , String
testcfile
            , String
"-o"
            , String
testofile
            ]
          String -> String -> (String -> Handle -> IO Bool) -> IO Bool
forall a. String -> String -> (String -> Handle -> IO a) -> IO a
withTempFile String
tempDir String
".o" ((String -> Handle -> IO Bool) -> IO Bool)
-> (String -> Handle -> IO Bool) -> IO Bool
forall a b. (a -> b) -> a -> b
$ \String
testofile' Handle
testohnd' ->
            do
              Handle -> IO ()
hClose Handle
testohnd'
              String
_ <-
                Verbosity -> ConfiguredProgram -> [String] -> IO String
getProgramOutput
                  Verbosity
verbosity
                  ConfiguredProgram
ldProg
                  [String
"-x", String
"-r", String
testofile, String
"-o", String
testofile']
              Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
True
              IO Bool -> (IOException -> IO Bool) -> IO Bool
forall a. IO a -> (IOException -> IO a) -> IO a
`catchIO` (\IOException
_ -> Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False)
              IO Bool -> (ExitCode -> IO Bool) -> IO Bool
forall a. IO a -> (ExitCode -> IO a) -> IO a
`catchExit` (\ExitCode
_ -> Bool -> IO Bool
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
False)
      if Bool
ldx
        then ConfiguredProgram -> IO ConfiguredProgram
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ConfiguredProgram
ldProg{programDefaultArgs = ["-x"]}
        else ConfiguredProgram -> IO ConfiguredProgram
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ConfiguredProgram
ldProg

getLanguages
  :: Verbosity
  -> GhcImplInfo
  -> ConfiguredProgram
  -> IO [(Language, String)]
getLanguages :: Verbosity
-> GhcImplInfo -> ConfiguredProgram -> IO [(Language, String)]
getLanguages Verbosity
_ GhcImplInfo
implInfo ConfiguredProgram
_
  -- TODO: should be using --supported-languages rather than hard coding
  | GhcImplInfo -> Bool
supportsGHC2021 GhcImplInfo
implInfo =
      [(Language, String)] -> IO [(Language, String)]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return
        [ (Language
GHC2021, String
"-XGHC2021")
        , (Language
Haskell2010, String
"-XHaskell2010")
        , (Language
Haskell98, String
"-XHaskell98")
        ]
  | GhcImplInfo -> Bool
supportsHaskell2010 GhcImplInfo
implInfo =
      [(Language, String)] -> IO [(Language, String)]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return
        [ (Language
Haskell98, String
"-XHaskell98")
        , (Language
Haskell2010, String
"-XHaskell2010")
        ]
  | Bool
otherwise = [(Language, String)] -> IO [(Language, String)]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [(Language
Haskell98, String
"")]

getGhcInfo
  :: Verbosity
  -> GhcImplInfo
  -> ConfiguredProgram
  -> IO [(String, String)]
getGhcInfo :: Verbosity
-> GhcImplInfo -> ConfiguredProgram -> IO [(String, String)]
getGhcInfo Verbosity
verbosity GhcImplInfo
_implInfo ConfiguredProgram
ghcProg = do
  String
xs <-
    Verbosity -> ConfiguredProgram -> [String] -> IO String
getProgramOutput
      Verbosity
verbosity
      (ConfiguredProgram -> ConfiguredProgram
suppressOverrideArgs ConfiguredProgram
ghcProg)
      [String
"--info"]
  case ReadS [(String, String)]
forall a. Read a => ReadS a
reads String
xs of
    [([(String, String)]
i, String
ss)]
      | (Char -> Bool) -> String -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all Char -> Bool
isSpace String
ss ->
          [(String, String)] -> IO [(String, String)]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [(String, String)]
i
    [([(String, String)], String)]
_ ->
      Verbosity -> String -> IO [(String, String)]
forall a. Verbosity -> String -> IO a
die' Verbosity
verbosity String
"Can't parse --info output of GHC"

getExtensions
  :: Verbosity
  -> GhcImplInfo
  -> ConfiguredProgram
  -> IO [(Extension, Maybe String)]
getExtensions :: Verbosity
-> GhcImplInfo
-> ConfiguredProgram
-> IO [(Extension, Maybe String)]
getExtensions Verbosity
verbosity GhcImplInfo
implInfo ConfiguredProgram
ghcProg = do
  String
str <-
    Verbosity -> ConfiguredProgram -> [String] -> IO String
getProgramOutput
      Verbosity
verbosity
      (ConfiguredProgram -> ConfiguredProgram
suppressOverrideArgs ConfiguredProgram
ghcProg)
      [String
"--supported-languages"]
  let extStrs :: [String]
extStrs =
        if GhcImplInfo -> Bool
reportsNoExt GhcImplInfo
implInfo
          then String -> [String]
lines String
str
          else -- Older GHCs only gave us either Foo or NoFoo,
          -- so we have to work out the other one ourselves

            [ String
extStr''
            | String
extStr <- String -> [String]
lines String
str
            , let extStr' :: String
extStr' = case String
extStr of
                    Char
'N' : Char
'o' : String
xs -> String
xs
                    String
_ -> String
"No" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
extStr
            , String
extStr'' <- [String
extStr, String
extStr']
            ]
  let extensions0 :: [(Extension, Maybe String)]
extensions0 =
        [ (Extension
ext, String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ String
"-X" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Extension -> String
forall a. Pretty a => a -> String
prettyShow Extension
ext)
        | Just Extension
ext <- (String -> Maybe Extension) -> [String] -> [Maybe Extension]
forall a b. (a -> b) -> [a] -> [b]
map String -> Maybe Extension
forall a. Parsec a => String -> Maybe a
simpleParsec [String]
extStrs
        ]
      extensions1 :: [(Extension, Maybe String)]
extensions1 =
        if GhcImplInfo -> Bool
alwaysNondecIndent GhcImplInfo
implInfo
          then -- ghc-7.2 split NondecreasingIndentation off
          -- into a proper extension. Before that it
          -- was always on.
          -- Since it was not a proper extension, it could
          -- not be turned off, hence we omit a
          -- DisableExtension entry here.

            (KnownExtension -> Extension
EnableExtension KnownExtension
NondecreasingIndentation, Maybe String
forall a. Maybe a
Nothing)
              (Extension, Maybe String)
-> [(Extension, Maybe String)] -> [(Extension, Maybe String)]
forall a. a -> [a] -> [a]
: [(Extension, Maybe String)]
extensions0
          else [(Extension, Maybe String)]
extensions0
  [(Extension, Maybe String)] -> IO [(Extension, Maybe String)]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [(Extension, Maybe String)]
extensions1

componentCcGhcOptions
  :: Verbosity
  -> GhcImplInfo
  -> LocalBuildInfo
  -> BuildInfo
  -> ComponentLocalBuildInfo
  -> FilePath
  -> FilePath
  -> GhcOptions
componentCcGhcOptions :: Verbosity
-> GhcImplInfo
-> LocalBuildInfo
-> BuildInfo
-> ComponentLocalBuildInfo
-> String
-> String
-> GhcOptions
componentCcGhcOptions Verbosity
verbosity GhcImplInfo
_implInfo LocalBuildInfo
lbi BuildInfo
bi ComponentLocalBuildInfo
clbi String
odir String
filename =
  GhcOptions
forall a. Monoid a => a
mempty
    { -- Respect -v0, but don't crank up verbosity on GHC if
      -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!
      ghcOptVerbosity = toFlag (min verbosity normal)
    , ghcOptMode = toFlag GhcModeCompile
    , ghcOptInputFiles = toNubListR [filename]
    , ghcOptCppIncludePath =
        toNubListR $
          [ autogenComponentModulesDir lbi clbi
          , autogenPackageModulesDir lbi
          , odir
          ]
            -- includes relative to the package
            ++ includeDirs bi
            -- potential includes generated by `configure'
            -- in the build directory
            ++ [buildDir lbi </> dir | dir <- includeDirs bi]
    , ghcOptHideAllPackages = toFlag True
    , ghcOptPackageDBs = withPackageDB lbi
    , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi
    , ghcOptCcOptions =
        ( case withOptimization lbi of
            OptimisationLevel
NoOptimisation -> []
            OptimisationLevel
_ -> [String
"-O2"]
        )
          ++ ( case withDebugInfo lbi of
                DebugInfoLevel
NoDebugInfo -> []
                DebugInfoLevel
MinimalDebugInfo -> [String
"-g1"]
                DebugInfoLevel
NormalDebugInfo -> [String
"-g"]
                DebugInfoLevel
MaximalDebugInfo -> [String
"-g3"]
             )
          ++ ccOptions bi
    , ghcOptCcProgram =
        maybeToFlag $
          programPath
            <$> lookupProgram gccProgram (withPrograms lbi)
    , ghcOptObjDir = toFlag odir
    , ghcOptExtra = hcOptions GHC bi
    }

componentCxxGhcOptions
  :: Verbosity
  -> GhcImplInfo
  -> LocalBuildInfo
  -> BuildInfo
  -> ComponentLocalBuildInfo
  -> FilePath
  -> FilePath
  -> GhcOptions
componentCxxGhcOptions :: Verbosity
-> GhcImplInfo
-> LocalBuildInfo
-> BuildInfo
-> ComponentLocalBuildInfo
-> String
-> String
-> GhcOptions
componentCxxGhcOptions Verbosity
verbosity GhcImplInfo
_implInfo LocalBuildInfo
lbi BuildInfo
bi ComponentLocalBuildInfo
clbi String
odir String
filename =
  GhcOptions
forall a. Monoid a => a
mempty
    { -- Respect -v0, but don't crank up verbosity on GHC if
      -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!
      ghcOptVerbosity = toFlag (min verbosity normal)
    , ghcOptMode = toFlag GhcModeCompile
    , ghcOptInputFiles = toNubListR [filename]
    , ghcOptCppIncludePath =
        toNubListR $
          [ autogenComponentModulesDir lbi clbi
          , autogenPackageModulesDir lbi
          , odir
          ]
            -- includes relative to the package
            ++ includeDirs bi
            -- potential includes generated by `configure'
            -- in the build directory
            ++ [buildDir lbi </> dir | dir <- includeDirs bi]
    , ghcOptHideAllPackages = toFlag True
    , ghcOptPackageDBs = withPackageDB lbi
    , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi
    , ghcOptCxxOptions =
        ( case withOptimization lbi of
            OptimisationLevel
NoOptimisation -> []
            OptimisationLevel
_ -> [String
"-O2"]
        )
          ++ ( case withDebugInfo lbi of
                DebugInfoLevel
NoDebugInfo -> []
                DebugInfoLevel
MinimalDebugInfo -> [String
"-g1"]
                DebugInfoLevel
NormalDebugInfo -> [String
"-g"]
                DebugInfoLevel
MaximalDebugInfo -> [String
"-g3"]
             )
          ++ cxxOptions bi
    , ghcOptCcProgram =
        maybeToFlag $
          programPath
            <$> lookupProgram gccProgram (withPrograms lbi)
    , ghcOptObjDir = toFlag odir
    , ghcOptExtra = hcOptions GHC bi
    }

componentAsmGhcOptions
  :: Verbosity
  -> GhcImplInfo
  -> LocalBuildInfo
  -> BuildInfo
  -> ComponentLocalBuildInfo
  -> FilePath
  -> FilePath
  -> GhcOptions
componentAsmGhcOptions :: Verbosity
-> GhcImplInfo
-> LocalBuildInfo
-> BuildInfo
-> ComponentLocalBuildInfo
-> String
-> String
-> GhcOptions
componentAsmGhcOptions Verbosity
verbosity GhcImplInfo
_implInfo LocalBuildInfo
lbi BuildInfo
bi ComponentLocalBuildInfo
clbi String
odir String
filename =
  GhcOptions
forall a. Monoid a => a
mempty
    { -- Respect -v0, but don't crank up verbosity on GHC if
      -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!
      ghcOptVerbosity = toFlag (min verbosity normal)
    , ghcOptMode = toFlag GhcModeCompile
    , ghcOptInputFiles = toNubListR [filename]
    , ghcOptCppIncludePath =
        toNubListR $
          [ autogenComponentModulesDir lbi clbi
          , autogenPackageModulesDir lbi
          , odir
          ]
            -- includes relative to the package
            ++ includeDirs bi
            -- potential includes generated by `configure'
            -- in the build directory
            ++ [buildDir lbi </> dir | dir <- includeDirs bi]
    , ghcOptHideAllPackages = toFlag True
    , ghcOptPackageDBs = withPackageDB lbi
    , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi
    , ghcOptAsmOptions =
        ( case withOptimization lbi of
            OptimisationLevel
NoOptimisation -> []
            OptimisationLevel
_ -> [String
"-O2"]
        )
          ++ ( case withDebugInfo lbi of
                DebugInfoLevel
NoDebugInfo -> []
                DebugInfoLevel
MinimalDebugInfo -> [String
"-g1"]
                DebugInfoLevel
NormalDebugInfo -> [String
"-g"]
                DebugInfoLevel
MaximalDebugInfo -> [String
"-g3"]
             )
          ++ asmOptions bi
    , ghcOptObjDir = toFlag odir
    }

componentJsGhcOptions
  :: Verbosity
  -> GhcImplInfo
  -> LocalBuildInfo
  -> BuildInfo
  -> ComponentLocalBuildInfo
  -> FilePath
  -> FilePath
  -> GhcOptions
componentJsGhcOptions :: Verbosity
-> GhcImplInfo
-> LocalBuildInfo
-> BuildInfo
-> ComponentLocalBuildInfo
-> String
-> String
-> GhcOptions
componentJsGhcOptions Verbosity
verbosity GhcImplInfo
_implInfo LocalBuildInfo
lbi BuildInfo
bi ComponentLocalBuildInfo
clbi String
odir String
filename =
  GhcOptions
forall a. Monoid a => a
mempty
    { -- Respect -v0, but don't crank up verbosity on GHC if
      -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!
      ghcOptVerbosity = toFlag (min verbosity normal)
    , ghcOptMode = toFlag GhcModeCompile
    , ghcOptInputFiles = toNubListR [filename]
    , ghcOptCppIncludePath =
        toNubListR $
          [ autogenComponentModulesDir lbi clbi
          , autogenPackageModulesDir lbi
          , odir
          ]
            -- includes relative to the package
            ++ includeDirs bi
            -- potential includes generated by `configure'
            -- in the build directory
            ++ [buildDir lbi </> dir | dir <- includeDirs bi]
    , ghcOptHideAllPackages = toFlag True
    , ghcOptPackageDBs = withPackageDB lbi
    , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi
    , ghcOptObjDir = toFlag odir
    }

componentGhcOptions
  :: Verbosity
  -> GhcImplInfo
  -> LocalBuildInfo
  -> BuildInfo
  -> ComponentLocalBuildInfo
  -> FilePath
  -> GhcOptions
componentGhcOptions :: Verbosity
-> GhcImplInfo
-> LocalBuildInfo
-> BuildInfo
-> ComponentLocalBuildInfo
-> String
-> GhcOptions
componentGhcOptions Verbosity
verbosity GhcImplInfo
implInfo LocalBuildInfo
lbi BuildInfo
bi ComponentLocalBuildInfo
clbi String
odir =
  GhcOptions
forall a. Monoid a => a
mempty
    { -- Respect -v0, but don't crank up verbosity on GHC if
      -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!
      ghcOptVerbosity = toFlag (min verbosity normal)
    , ghcOptCabal = toFlag True
    , ghcOptThisUnitId = case clbi of
        LibComponentLocalBuildInfo{componentCompatPackageKey :: ComponentLocalBuildInfo -> String
componentCompatPackageKey = String
pk} ->
          String -> Flag String
forall a. a -> Flag a
toFlag String
pk
        ComponentLocalBuildInfo
_ | Bool -> Bool
not (GhcImplInfo -> Bool
unitIdForExes GhcImplInfo
implInfo) -> Flag String
forall a. Monoid a => a
mempty
        ExeComponentLocalBuildInfo{componentUnitId :: ComponentLocalBuildInfo -> UnitId
componentUnitId = UnitId
uid} ->
          String -> Flag String
forall a. a -> Flag a
toFlag (UnitId -> String
unUnitId UnitId
uid)
        TestComponentLocalBuildInfo{componentUnitId :: ComponentLocalBuildInfo -> UnitId
componentUnitId = UnitId
uid} ->
          String -> Flag String
forall a. a -> Flag a
toFlag (UnitId -> String
unUnitId UnitId
uid)
        BenchComponentLocalBuildInfo{componentUnitId :: ComponentLocalBuildInfo -> UnitId
componentUnitId = UnitId
uid} ->
          String -> Flag String
forall a. a -> Flag a
toFlag (UnitId -> String
unUnitId UnitId
uid)
        FLibComponentLocalBuildInfo{componentUnitId :: ComponentLocalBuildInfo -> UnitId
componentUnitId = UnitId
uid} ->
          String -> Flag String
forall a. a -> Flag a
toFlag (UnitId -> String
unUnitId UnitId
uid)
    , ghcOptThisComponentId = case clbi of
        LibComponentLocalBuildInfo
          { componentComponentId :: ComponentLocalBuildInfo -> ComponentId
componentComponentId = ComponentId
cid
          , componentInstantiatedWith :: ComponentLocalBuildInfo -> [(ModuleName, OpenModule)]
componentInstantiatedWith = [(ModuleName, OpenModule)]
insts
          } ->
            if [(ModuleName, OpenModule)] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(ModuleName, OpenModule)]
insts
              then Flag ComponentId
forall a. Monoid a => a
mempty
              else ComponentId -> Flag ComponentId
forall a. a -> Flag a
toFlag ComponentId
cid
        ComponentLocalBuildInfo
_ -> Flag ComponentId
forall a. Monoid a => a
mempty
    , ghcOptInstantiatedWith = case clbi of
        LibComponentLocalBuildInfo{componentInstantiatedWith :: ComponentLocalBuildInfo -> [(ModuleName, OpenModule)]
componentInstantiatedWith = [(ModuleName, OpenModule)]
insts} ->
          [(ModuleName, OpenModule)]
insts
        ComponentLocalBuildInfo
_ -> []
    , ghcOptNoCode = toFlag $ componentIsIndefinite clbi
    , ghcOptHideAllPackages = toFlag True
    , ghcOptWarnMissingHomeModules = toFlag $ flagWarnMissingHomeModules implInfo
    , ghcOptPackageDBs = withPackageDB lbi
    , ghcOptPackages = toNubListR $ mkGhcOptPackages mempty clbi
    , ghcOptSplitSections = toFlag (splitSections lbi)
    , ghcOptSplitObjs = toFlag (splitObjs lbi)
    , ghcOptSourcePathClear = toFlag True
    , ghcOptSourcePath =
        toNubListR $
          map getSymbolicPath (hsSourceDirs bi)
            ++ [odir]
            ++ [autogenComponentModulesDir lbi clbi]
            ++ [autogenPackageModulesDir lbi]
    , ghcOptCppIncludePath =
        toNubListR $
          [ autogenComponentModulesDir lbi clbi
          , autogenPackageModulesDir lbi
          , odir
          ]
            -- includes relative to the package
            ++ includeDirs bi
            -- potential includes generated by `configure'
            -- in the build directory
            ++ [buildDir lbi </> dir | dir <- includeDirs bi]
    , ghcOptCppOptions = cppOptions bi
    , ghcOptCppIncludes =
        toNubListR $
          [autogenComponentModulesDir lbi clbi </> cppHeaderName]
    , ghcOptFfiIncludes = toNubListR $ includes bi
    , ghcOptObjDir = toFlag odir
    , ghcOptHiDir = toFlag odir
    , ghcOptStubDir = toFlag odir
    , ghcOptOutputDir = toFlag odir
    , ghcOptOptimisation = toGhcOptimisation (withOptimization lbi)
    , ghcOptDebugInfo = toFlag (withDebugInfo lbi)
    , ghcOptExtra = hcOptions GHC bi
    , ghcOptExtraPath = toNubListR $ exe_paths
    , ghcOptLanguage = toFlag (fromMaybe Haskell98 (defaultLanguage bi))
    , -- Unsupported extensions have already been checked by configure
      ghcOptExtensions = toNubListR $ usedExtensions bi
    , ghcOptExtensionMap = Map.fromList . compilerExtensions $ (compiler lbi)
    }
  where
    exe_paths :: [String]
exe_paths =
      [ LocalBuildInfo -> ComponentLocalBuildInfo -> String
componentBuildDir LocalBuildInfo
lbi (TargetInfo -> ComponentLocalBuildInfo
targetCLBI TargetInfo
exe_tgt)
      | UnitId
uid <- ComponentLocalBuildInfo -> [UnitId]
componentExeDeps ComponentLocalBuildInfo
clbi
      , -- TODO: Ugh, localPkgDescr
      Just TargetInfo
exe_tgt <- [PackageDescription -> LocalBuildInfo -> UnitId -> Maybe TargetInfo
unitIdTarget' (LocalBuildInfo -> PackageDescription
localPkgDescr LocalBuildInfo
lbi) LocalBuildInfo
lbi UnitId
uid]
      ]

toGhcOptimisation :: OptimisationLevel -> Flag GhcOptimisation
toGhcOptimisation :: OptimisationLevel -> Flag GhcOptimisation
toGhcOptimisation OptimisationLevel
NoOptimisation = Flag GhcOptimisation
forall a. Monoid a => a
mempty -- TODO perhaps override?
toGhcOptimisation OptimisationLevel
NormalOptimisation = GhcOptimisation -> Flag GhcOptimisation
forall a. a -> Flag a
toFlag GhcOptimisation
GhcNormalOptimisation
toGhcOptimisation OptimisationLevel
MaximumOptimisation = GhcOptimisation -> Flag GhcOptimisation
forall a. a -> Flag a
toFlag GhcOptimisation
GhcMaximumOptimisation

componentCmmGhcOptions
  :: Verbosity
  -> GhcImplInfo
  -> LocalBuildInfo
  -> BuildInfo
  -> ComponentLocalBuildInfo
  -> FilePath
  -> FilePath
  -> GhcOptions
componentCmmGhcOptions :: Verbosity
-> GhcImplInfo
-> LocalBuildInfo
-> BuildInfo
-> ComponentLocalBuildInfo
-> String
-> String
-> GhcOptions
componentCmmGhcOptions Verbosity
verbosity GhcImplInfo
_implInfo LocalBuildInfo
lbi BuildInfo
bi ComponentLocalBuildInfo
clbi String
odir String
filename =
  GhcOptions
forall a. Monoid a => a
mempty
    { -- Respect -v0, but don't crank up verbosity on GHC if
      -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!
      ghcOptVerbosity = toFlag (min verbosity normal)
    , ghcOptMode = toFlag GhcModeCompile
    , ghcOptInputFiles = toNubListR [filename]
    , ghcOptCppIncludePath =
        toNubListR $
          [ autogenComponentModulesDir lbi clbi
          , autogenPackageModulesDir lbi
          , odir
          ]
            -- includes relative to the package
            ++ includeDirs bi
            -- potential includes generated by `configure'
            -- in the build directory
            ++ [buildDir lbi </> dir | dir <- includeDirs bi]
    , ghcOptCppOptions = cppOptions bi
    , ghcOptCppIncludes =
        toNubListR $
          [autogenComponentModulesDir lbi clbi </> cppHeaderName]
    , ghcOptHideAllPackages = toFlag True
    , ghcOptPackageDBs = withPackageDB lbi
    , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi
    , ghcOptOptimisation = toGhcOptimisation (withOptimization lbi)
    , ghcOptDebugInfo = toFlag (withDebugInfo lbi)
    , ghcOptExtra = cmmOptions bi
    , ghcOptObjDir = toFlag odir
    }

-- | Strip out flags that are not supported in ghci
filterGhciFlags :: [String] -> [String]
filterGhciFlags :: [String] -> [String]
filterGhciFlags = (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter String -> Bool
supported
  where
    supported :: String -> Bool
supported (Char
'-' : Char
'O' : String
_) = Bool
False
    supported String
"-debug" = Bool
False
    supported String
"-threaded" = Bool
False
    supported String
"-ticky" = Bool
False
    supported String
"-eventlog" = Bool
False
    supported String
"-prof" = Bool
False
    supported String
"-unreg" = Bool
False
    supported String
_ = Bool
True

mkGHCiLibName :: UnitId -> String
mkGHCiLibName :: UnitId -> String
mkGHCiLibName UnitId
lib = UnitId -> String
getHSLibraryName UnitId
lib String -> String -> String
<.> String
"o"

mkGHCiProfLibName :: UnitId -> String
mkGHCiProfLibName :: UnitId -> String
mkGHCiProfLibName UnitId
lib = UnitId -> String
getHSLibraryName UnitId
lib String -> String -> String
<.> String
"p_o"

ghcLookupProperty :: String -> Compiler -> Bool
ghcLookupProperty :: String -> Compiler -> Bool
ghcLookupProperty String
prop Compiler
comp =
  case String -> Map String String -> Maybe String
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup String
prop (Compiler -> Map String String
compilerProperties Compiler
comp) of
    Just String
"YES" -> Bool
True
    Maybe String
_ -> Bool
False

-- when using -split-objs, we need to search for object files in the
-- Module_split directory for each module.
getHaskellObjects
  :: GhcImplInfo
  -> Library
  -> LocalBuildInfo
  -> ComponentLocalBuildInfo
  -> FilePath
  -> String
  -> Bool
  -> IO [FilePath]
getHaskellObjects :: GhcImplInfo
-> Library
-> LocalBuildInfo
-> ComponentLocalBuildInfo
-> String
-> String
-> Bool
-> IO [String]
getHaskellObjects GhcImplInfo
_implInfo Library
lib LocalBuildInfo
lbi ComponentLocalBuildInfo
clbi String
pref String
wanted_obj_ext Bool
allow_split_objs
  | LocalBuildInfo -> Bool
splitObjs LocalBuildInfo
lbi Bool -> Bool -> Bool
&& Bool
allow_split_objs = do
      let splitSuffix :: String
splitSuffix = String
"_" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
wanted_obj_ext String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"_split"
          dirs :: [String]
dirs =
            [ String
pref String -> String -> String
</> (ModuleName -> String
ModuleName.toFilePath ModuleName
x String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
splitSuffix)
            | ModuleName
x <- Library -> ComponentLocalBuildInfo -> [ModuleName]
allLibModules Library
lib ComponentLocalBuildInfo
clbi
            ]
      [[String]]
objss <- (String -> IO [String]) -> [String] -> IO [[String]]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse String -> IO [String]
getDirectoryContents [String]
dirs
      let objs :: [String]
objs =
            [ String
dir String -> String -> String
</> String
obj
            | ([String]
objs', String
dir) <- [[String]] -> [String] -> [([String], String)]
forall a b. [a] -> [b] -> [(a, b)]
zip [[String]]
objss [String]
dirs
            , String
obj <- [String]
objs'
            , let obj_ext :: String
obj_ext = String -> String
takeExtension String
obj
            , Char
'.' Char -> String -> String
forall a. a -> [a] -> [a]
: String
wanted_obj_ext String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
obj_ext
            ]
      [String] -> IO [String]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return [String]
objs
  | Bool
otherwise =
      [String] -> IO [String]
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return
        [ String
pref String -> String -> String
</> ModuleName -> String
ModuleName.toFilePath ModuleName
x String -> String -> String
<.> String
wanted_obj_ext
        | ModuleName
x <- Library -> ComponentLocalBuildInfo -> [ModuleName]
allLibModules Library
lib ComponentLocalBuildInfo
clbi
        ]

-- | Create the required packaged arguments, but filtering out package arguments which
-- aren't yet built, but promised. This filtering is used when compiling C/Cxx/Asm files,
-- and is a hack to avoid passing bogus `-package` arguments to GHC. The assumption being that
-- in 99% of cases we will include the right `-package` so that the C file finds the right headers.
mkGhcOptPackages
  :: Map (PackageName, ComponentName) ComponentId
  -> ComponentLocalBuildInfo
  -> [(OpenUnitId, ModuleRenaming)]
mkGhcOptPackages :: Map (PackageName, ComponentName) ComponentId
-> ComponentLocalBuildInfo -> [(OpenUnitId, ModuleRenaming)]
mkGhcOptPackages Map (PackageName, ComponentName) ComponentId
promisedPkgsMap ComponentLocalBuildInfo
clbi =
  [ (OpenUnitId, ModuleRenaming)
i | i :: (OpenUnitId, ModuleRenaming)
i@(OpenUnitId
uid, ModuleRenaming
_) <- ComponentLocalBuildInfo -> [(OpenUnitId, ModuleRenaming)]
componentIncludes ComponentLocalBuildInfo
clbi, OpenUnitId -> UnitId
abstractUnitId OpenUnitId
uid UnitId -> Set UnitId -> Bool
forall a. Ord a => a -> Set a -> Bool
`Set.notMember` Set UnitId
promised_cids
  ]
  where
    -- Promised deps are going to be simple UnitIds
    promised_cids :: Set UnitId
promised_cids = [UnitId] -> Set UnitId
forall a. Ord a => [a] -> Set a
Set.fromList ((ComponentId -> UnitId) -> [ComponentId] -> [UnitId]
forall a b. (a -> b) -> [a] -> [b]
map ComponentId -> UnitId
newSimpleUnitId (Map (PackageName, ComponentName) ComponentId -> [ComponentId]
forall k a. Map k a -> [a]
Map.elems Map (PackageName, ComponentName) ComponentId
promisedPkgsMap))

substTopDir :: FilePath -> IPI.InstalledPackageInfo -> IPI.InstalledPackageInfo
substTopDir :: String -> InstalledPackageInfo -> InstalledPackageInfo
substTopDir String
topDir InstalledPackageInfo
ipo =
  InstalledPackageInfo
ipo
    { IPI.importDirs = map f (IPI.importDirs ipo)
    , IPI.libraryDirs = map f (IPI.libraryDirs ipo)
    , IPI.libraryDirsStatic = map f (IPI.libraryDirsStatic ipo)
    , IPI.includeDirs = map f (IPI.includeDirs ipo)
    , IPI.frameworkDirs = map f (IPI.frameworkDirs ipo)
    , IPI.haddockInterfaces = map f (IPI.haddockInterfaces ipo)
    , IPI.haddockHTMLs = map f (IPI.haddockHTMLs ipo)
    }
  where
    f :: String -> String
f (Char
'$' : Char
't' : Char
'o' : Char
'p' : Char
'd' : Char
'i' : Char
'r' : String
rest) = String
topDir String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
rest
    f String
x = String
x

-- Cabal does not use the environment variable GHC{,JS}_PACKAGE_PATH; let
-- users know that this is the case. See ticket #335. Simply ignoring it is
-- not a good idea, since then ghc and cabal are looking at different sets
-- of package DBs and chaos is likely to ensue.
--
-- An exception to this is when running cabal from within a `cabal exec`
-- environment. In this case, `cabal exec` will set the
-- CABAL_SANDBOX_PACKAGE_PATH to the same value that it set
-- GHC{,JS}_PACKAGE_PATH to. If that is the case it is OK to allow
-- GHC{,JS}_PACKAGE_PATH.
checkPackageDbEnvVar :: Verbosity -> String -> String -> IO ()
checkPackageDbEnvVar :: Verbosity -> String -> String -> IO ()
checkPackageDbEnvVar Verbosity
verbosity String
compilerName String
packagePathEnvVar = do
  Maybe String
mPP <- String -> IO (Maybe String)
lookupEnv String
packagePathEnvVar
  Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Maybe String -> Bool
forall a. Maybe a -> Bool
isJust Maybe String
mPP) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
    Maybe String
mcsPP <- String -> IO (Maybe String)
lookupEnv String
"CABAL_SANDBOX_PACKAGE_PATH"
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Maybe String
mPP Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe String
mcsPP) IO ()
forall {a}. IO a
abort
  where
    lookupEnv :: String -> IO (Maybe String)
    lookupEnv :: String -> IO (Maybe String)
lookupEnv String
name =
      (String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> IO String -> IO (Maybe String)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` String -> IO String
getEnv String
name)
        IO (Maybe String)
-> (IOException -> IO (Maybe String)) -> IO (Maybe String)
forall a. IO a -> (IOException -> IO a) -> IO a
`catchIO` IO (Maybe String) -> IOException -> IO (Maybe String)
forall a b. a -> b -> a
const (Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe String
forall a. Maybe a
Nothing)
    abort :: IO a
abort =
      Verbosity -> String -> IO a
forall a. Verbosity -> String -> IO a
die' Verbosity
verbosity (String -> IO a) -> String -> IO a
forall a b. (a -> b) -> a -> b
$
        String
"Use of "
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
compilerName
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"'s environment variable "
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
packagePathEnvVar
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" is incompatible with Cabal. Use the "
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"flag --package-db to specify a package database (it can be "
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"used multiple times)."

    CallStack
_ = CallStack
HasCallStack => CallStack
callStack -- TODO: output stack when erroring

profDetailLevelFlag :: Bool -> ProfDetailLevel -> Flag GhcProfAuto
profDetailLevelFlag :: Bool -> ProfDetailLevel -> Flag GhcProfAuto
profDetailLevelFlag Bool
forLib ProfDetailLevel
mpl =
  case ProfDetailLevel
mpl of
    ProfDetailLevel
ProfDetailNone -> Flag GhcProfAuto
forall a. Monoid a => a
mempty
    ProfDetailLevel
ProfDetailDefault
      | Bool
forLib -> GhcProfAuto -> Flag GhcProfAuto
forall a. a -> Flag a
toFlag GhcProfAuto
GhcProfAutoExported
      | Bool
otherwise -> GhcProfAuto -> Flag GhcProfAuto
forall a. a -> Flag a
toFlag GhcProfAuto
GhcProfAutoToplevel
    ProfDetailLevel
ProfDetailExportedFunctions -> GhcProfAuto -> Flag GhcProfAuto
forall a. a -> Flag a
toFlag GhcProfAuto
GhcProfAutoExported
    ProfDetailLevel
ProfDetailToplevelFunctions -> GhcProfAuto -> Flag GhcProfAuto
forall a. a -> Flag a
toFlag GhcProfAuto
GhcProfAutoToplevel
    ProfDetailLevel
ProfDetailAllFunctions -> GhcProfAuto -> Flag GhcProfAuto
forall a. a -> Flag a
toFlag GhcProfAuto
GhcProfAutoAll
    ProfDetailLevel
ProfDetailTopLate -> GhcProfAuto -> Flag GhcProfAuto
forall a. a -> Flag a
toFlag GhcProfAuto
GhcProfLate
    ProfDetailOther String
_ -> Flag GhcProfAuto
forall a. Monoid a => a
mempty

-- -----------------------------------------------------------------------------
-- GHC platform and version strings

-- | GHC's rendering of its host or target 'Arch' as used in its platform
-- strings and certain file locations (such as user package db location).
ghcArchString :: Arch -> String
ghcArchString :: Arch -> String
ghcArchString Arch
PPC = String
"powerpc"
ghcArchString Arch
PPC64 = String
"powerpc64"
ghcArchString Arch
other = Arch -> String
forall a. Pretty a => a -> String
prettyShow Arch
other

-- | GHC's rendering of its host or target 'OS' as used in its platform
-- strings and certain file locations (such as user package db location).
ghcOsString :: OS -> String
ghcOsString :: OS -> String
ghcOsString OS
Windows = String
"mingw32"
ghcOsString OS
OSX = String
"darwin"
ghcOsString OS
Solaris = String
"solaris2"
ghcOsString OS
other = OS -> String
forall a. Pretty a => a -> String
prettyShow OS
other

-- | GHC's rendering of its platform and compiler version string as used in
-- certain file locations (such as user package db location).
-- For example @x86_64-linux-7.10.4@
ghcPlatformAndVersionString :: Platform -> Version -> String
ghcPlatformAndVersionString :: Platform -> Version -> String
ghcPlatformAndVersionString (Platform Arch
arch OS
os) Version
version =
  String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"-" [Arch -> String
ghcArchString Arch
arch, OS -> String
ghcOsString OS
os, Version -> String
forall a. Pretty a => a -> String
prettyShow Version
version]

-- -----------------------------------------------------------------------------
-- Constructing GHC environment files

-- | The kinds of entries we can stick in a @.ghc.environment@ file.
data GhcEnvironmentFileEntry
  = -- | @-- a comment@
    GhcEnvFileComment String
  | -- | @package-id foo-1.0-4fe301a...@
    GhcEnvFilePackageId UnitId
  | -- | @global-package-db@,
    --   @user-package-db@ or
    --   @package-db blah/package.conf.d/@
    GhcEnvFilePackageDb PackageDB
  | -- | @clear-package-db@
    GhcEnvFileClearPackageDbStack
  deriving (GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
(GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool)
-> (GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool)
-> Eq GhcEnvironmentFileEntry
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
== :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
$c/= :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
/= :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
Eq, Eq GhcEnvironmentFileEntry
Eq GhcEnvironmentFileEntry =>
(GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Ordering)
-> (GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool)
-> (GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool)
-> (GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool)
-> (GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool)
-> (GhcEnvironmentFileEntry
    -> GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry)
-> (GhcEnvironmentFileEntry
    -> GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry)
-> Ord GhcEnvironmentFileEntry
GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Ordering
GhcEnvironmentFileEntry
-> GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry
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 :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Ordering
compare :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Ordering
$c< :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
< :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
$c<= :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
<= :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
$c> :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
> :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
$c>= :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
>= :: GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry -> Bool
$cmax :: GhcEnvironmentFileEntry
-> GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry
max :: GhcEnvironmentFileEntry
-> GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry
$cmin :: GhcEnvironmentFileEntry
-> GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry
min :: GhcEnvironmentFileEntry
-> GhcEnvironmentFileEntry -> GhcEnvironmentFileEntry
Ord, Int -> GhcEnvironmentFileEntry -> String -> String
[GhcEnvironmentFileEntry] -> String -> String
GhcEnvironmentFileEntry -> String
(Int -> GhcEnvironmentFileEntry -> String -> String)
-> (GhcEnvironmentFileEntry -> String)
-> ([GhcEnvironmentFileEntry] -> String -> String)
-> Show GhcEnvironmentFileEntry
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> GhcEnvironmentFileEntry -> String -> String
showsPrec :: Int -> GhcEnvironmentFileEntry -> String -> String
$cshow :: GhcEnvironmentFileEntry -> String
show :: GhcEnvironmentFileEntry -> String
$cshowList :: [GhcEnvironmentFileEntry] -> String -> String
showList :: [GhcEnvironmentFileEntry] -> String -> String
Show)

-- | Make entries for a GHC environment file based on a 'PackageDBStack' and
-- a bunch of package (unit) ids.
--
-- If you need to do anything more complicated then either use this as a basis
-- and add more entries, or just make all the entries directly.
simpleGhcEnvironmentFile
  :: PackageDBStack
  -> [UnitId]
  -> [GhcEnvironmentFileEntry]
simpleGhcEnvironmentFile :: PackageDBStack -> [UnitId] -> [GhcEnvironmentFileEntry]
simpleGhcEnvironmentFile PackageDBStack
packageDBs [UnitId]
pkgids =
  GhcEnvironmentFileEntry
GhcEnvFileClearPackageDbStack
    GhcEnvironmentFileEntry
-> [GhcEnvironmentFileEntry] -> [GhcEnvironmentFileEntry]
forall a. a -> [a] -> [a]
: (PackageDB -> GhcEnvironmentFileEntry)
-> PackageDBStack -> [GhcEnvironmentFileEntry]
forall a b. (a -> b) -> [a] -> [b]
map PackageDB -> GhcEnvironmentFileEntry
GhcEnvFilePackageDb PackageDBStack
packageDBs
    [GhcEnvironmentFileEntry]
-> [GhcEnvironmentFileEntry] -> [GhcEnvironmentFileEntry]
forall a. [a] -> [a] -> [a]
++ (UnitId -> GhcEnvironmentFileEntry)
-> [UnitId] -> [GhcEnvironmentFileEntry]
forall a b. (a -> b) -> [a] -> [b]
map UnitId -> GhcEnvironmentFileEntry
GhcEnvFilePackageId [UnitId]
pkgids

-- | Write a @.ghc.environment-$arch-$os-$ver@ file in the given directory.
--
-- The 'Platform' and GHC 'Version' are needed as part of the file name.
--
-- Returns the name of the file written.
writeGhcEnvironmentFile
  :: FilePath
  -- ^ directory in which to put it
  -> Platform
  -- ^ the GHC target platform
  -> Version
  -- ^ the GHC version
  -> [GhcEnvironmentFileEntry]
  -- ^ the content
  -> IO FilePath
writeGhcEnvironmentFile :: String
-> Platform -> Version -> [GhcEnvironmentFileEntry] -> IO String
writeGhcEnvironmentFile String
directory Platform
platform Version
ghcversion [GhcEnvironmentFileEntry]
entries = do
  String -> ByteString -> IO ()
writeFileAtomic String
envfile (ByteString -> IO ())
-> ([GhcEnvironmentFileEntry] -> ByteString)
-> [GhcEnvironmentFileEntry]
-> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ByteString
BS.pack (String -> ByteString)
-> ([GhcEnvironmentFileEntry] -> String)
-> [GhcEnvironmentFileEntry]
-> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [GhcEnvironmentFileEntry] -> String
renderGhcEnvironmentFile ([GhcEnvironmentFileEntry] -> IO ())
-> [GhcEnvironmentFileEntry] -> IO ()
forall a b. (a -> b) -> a -> b
$ [GhcEnvironmentFileEntry]
entries
  String -> IO String
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return String
envfile
  where
    envfile :: String
envfile = String
directory String -> String -> String
</> Platform -> Version -> String
ghcEnvironmentFileName Platform
platform Version
ghcversion

-- | The @.ghc.environment-$arch-$os-$ver@ file name
ghcEnvironmentFileName :: Platform -> Version -> FilePath
ghcEnvironmentFileName :: Platform -> Version -> String
ghcEnvironmentFileName Platform
platform Version
ghcversion =
  String
".ghc.environment." String -> String -> String
forall a. [a] -> [a] -> [a]
++ Platform -> Version -> String
ghcPlatformAndVersionString Platform
platform Version
ghcversion

-- | Render a bunch of GHC environment file entries
renderGhcEnvironmentFile :: [GhcEnvironmentFileEntry] -> String
renderGhcEnvironmentFile :: [GhcEnvironmentFileEntry] -> String
renderGhcEnvironmentFile =
  [String] -> String
unlines ([String] -> String)
-> ([GhcEnvironmentFileEntry] -> [String])
-> [GhcEnvironmentFileEntry]
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (GhcEnvironmentFileEntry -> String)
-> [GhcEnvironmentFileEntry] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map GhcEnvironmentFileEntry -> String
renderGhcEnvironmentFileEntry

-- | Render an individual GHC environment file entry
renderGhcEnvironmentFileEntry :: GhcEnvironmentFileEntry -> String
renderGhcEnvironmentFileEntry :: GhcEnvironmentFileEntry -> String
renderGhcEnvironmentFileEntry GhcEnvironmentFileEntry
entry = case GhcEnvironmentFileEntry
entry of
  GhcEnvFileComment String
comment -> String -> String
format String
comment
    where
      format :: String -> String
format = String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
"\n" ([String] -> String) -> (String -> [String]) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String
"--" String -> String -> String
<++>) ([String] -> [String])
-> (String -> [String]) -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
lines
      String
pref <++> :: String -> String -> String
<++> String
"" = String
pref
      String
pref <++> String
str = String
pref String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
str
  GhcEnvFilePackageId UnitId
pkgid -> String
"package-id " String -> String -> String
forall a. [a] -> [a] -> [a]
++ UnitId -> String
forall a. Pretty a => a -> String
prettyShow UnitId
pkgid
  GhcEnvFilePackageDb PackageDB
pkgdb ->
    case PackageDB
pkgdb of
      PackageDB
GlobalPackageDB -> String
"global-package-db"
      PackageDB
UserPackageDB -> String
"user-package-db"
      SpecificPackageDB String
dbfile -> String
"package-db " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
dbfile
  GhcEnvironmentFileEntry
GhcEnvFileClearPackageDbStack -> String
"clear-package-db"