{-# LANGUAGE DeriveGeneric #-}
module Distribution.Simple.Program.GHC (
GhcOptions(..),
GhcMode(..),
GhcOptimisation(..),
GhcDynLinkMode(..),
GhcProfAuto(..),
ghcInvocation,
renderGhcOptions,
runGHC,
) where
import Distribution.Compat.Semigroup as Semi
import Distribution.Simple.GHC.ImplInfo
import Distribution.Package
import Distribution.PackageDescription hiding (Flag)
import Distribution.ModuleName
import Distribution.Simple.Compiler hiding (Flag)
import Distribution.Simple.Setup
import Distribution.Simple.Program.Types
import Distribution.Simple.Program.Run
import Distribution.System
import Distribution.Text
import Distribution.Verbosity
import Distribution.Utils.NubList
import Language.Haskell.Extension
import GHC.Generics (Generic)
import qualified Data.Map as M
data GhcOptions = GhcOptions {
ghcOptMode :: Flag GhcMode,
ghcOptExtra :: NubListR String,
ghcOptExtraDefault :: NubListR String,
ghcOptInputFiles :: NubListR FilePath,
ghcOptInputModules :: NubListR ModuleName,
ghcOptOutputFile :: Flag FilePath,
ghcOptOutputDynFile :: Flag FilePath,
ghcOptSourcePathClear :: Flag Bool,
ghcOptSourcePath :: NubListR FilePath,
ghcOptThisUnitId :: Flag String,
ghcOptPackageDBs :: PackageDBStack,
ghcOptPackages ::
NubListR (UnitId, PackageId, ModuleRenaming),
ghcOptHideAllPackages :: Flag Bool,
ghcOptNoAutoLinkPackages :: Flag Bool,
ghcOptLinkLibs :: NubListR FilePath,
ghcOptLinkLibPath :: NubListR FilePath,
ghcOptLinkOptions :: NubListR String,
ghcOptLinkFrameworks :: NubListR String,
ghcOptLinkFrameworkDirs :: NubListR String,
ghcOptNoLink :: Flag Bool,
ghcOptLinkNoHsMain :: Flag Bool,
ghcOptCcOptions :: NubListR String,
ghcOptCppOptions :: NubListR String,
ghcOptCppIncludePath :: NubListR FilePath,
ghcOptCppIncludes :: NubListR FilePath,
ghcOptFfiIncludes :: NubListR FilePath,
ghcOptLanguage :: Flag Language,
ghcOptExtensions :: NubListR Extension,
ghcOptExtensionMap :: M.Map Extension String,
ghcOptOptimisation :: Flag GhcOptimisation,
ghcOptDebugInfo :: Flag Bool,
ghcOptProfilingMode :: Flag Bool,
ghcOptProfilingAuto :: Flag GhcProfAuto,
ghcOptSplitObjs :: Flag Bool,
ghcOptNumJobs :: Flag (Maybe Int),
ghcOptHPCDir :: Flag FilePath,
ghcOptGHCiScripts :: NubListR FilePath,
ghcOptHiSuffix :: Flag String,
ghcOptObjSuffix :: Flag String,
ghcOptDynHiSuffix :: Flag String,
ghcOptDynObjSuffix :: Flag String,
ghcOptHiDir :: Flag FilePath,
ghcOptObjDir :: Flag FilePath,
ghcOptOutputDir :: Flag FilePath,
ghcOptStubDir :: Flag FilePath,
ghcOptDynLinkMode :: Flag GhcDynLinkMode,
ghcOptShared :: Flag Bool,
ghcOptFPic :: Flag Bool,
ghcOptDylibName :: Flag String,
ghcOptRPaths :: NubListR FilePath,
ghcOptVerbosity :: Flag Verbosity,
ghcOptCabal :: Flag Bool
} deriving (Show, Generic)
data GhcMode = GhcModeCompile
| GhcModeLink
| GhcModeMake
| GhcModeInteractive
| GhcModeAbiHash
deriving (Show, Eq)
data GhcOptimisation = GhcNoOptimisation
| GhcNormalOptimisation
| GhcMaximumOptimisation
| GhcSpecialOptimisation String
deriving (Show, Eq)
data GhcDynLinkMode = GhcStaticOnly
| GhcDynamicOnly
| GhcStaticAndDynamic
deriving (Show, Eq)
data GhcProfAuto = GhcProfAutoAll
| GhcProfAutoToplevel
| GhcProfAutoExported
deriving (Show, Eq)
runGHC :: Verbosity -> ConfiguredProgram -> Compiler -> Platform -> GhcOptions
-> IO ()
runGHC verbosity ghcProg comp platform opts = do
runProgramInvocation verbosity (ghcInvocation ghcProg comp platform opts)
ghcInvocation :: ConfiguredProgram -> Compiler -> Platform -> GhcOptions
-> ProgramInvocation
ghcInvocation prog comp platform opts =
programInvocation prog (renderGhcOptions comp platform opts)
renderGhcOptions :: Compiler -> Platform -> GhcOptions -> [String]
renderGhcOptions comp _platform@(Platform _arch os) opts
| compilerFlavor comp `notElem` [GHC, GHCJS] =
error $ "Distribution.Simple.Program.GHC.renderGhcOptions: "
++ "compiler flavor must be 'GHC' or 'GHCJS'!"
| otherwise =
concat
[ case flagToMaybe (ghcOptMode opts) of
Nothing -> []
Just GhcModeCompile -> ["-c"]
Just GhcModeLink -> []
Just GhcModeMake -> ["--make"]
Just GhcModeInteractive -> ["--interactive"]
Just GhcModeAbiHash -> ["--abi-hash"]
, flags ghcOptExtraDefault
, [ "-no-link" | flagBool ghcOptNoLink ]
, maybe [] verbosityOpts (flagToMaybe (ghcOptVerbosity opts))
, [ "-fbuilding-cabal-package" | flagBool ghcOptCabal
, flagBuildingCabalPkg implInfo ]
, case flagToMaybe (ghcOptOptimisation opts) of
Nothing -> []
Just GhcNoOptimisation -> ["-O0"]
Just GhcNormalOptimisation -> ["-O"]
Just GhcMaximumOptimisation -> ["-O2"]
Just (GhcSpecialOptimisation s) -> ["-O" ++ s]
, [ "-g" | flagDebugInfo implInfo && flagBool ghcOptDebugInfo ]
, [ "-prof" | flagBool ghcOptProfilingMode ]
, case flagToMaybe (ghcOptProfilingAuto opts) of
_ | not (flagBool ghcOptProfilingMode)
-> []
Nothing -> []
Just GhcProfAutoAll
| flagProfAuto implInfo -> ["-fprof-auto"]
| otherwise -> ["-auto-all"]
Just GhcProfAutoToplevel
| flagProfAuto implInfo -> ["-fprof-auto-top"]
| otherwise -> ["-auto-all"]
Just GhcProfAutoExported
| flagProfAuto implInfo -> ["-fprof-auto-exported"]
| otherwise -> ["-auto"]
, [ "-split-objs" | flagBool ghcOptSplitObjs ]
, case flagToMaybe (ghcOptHPCDir opts) of
Nothing -> []
Just hpcdir -> ["-fhpc", "-hpcdir", hpcdir]
, if parmakeSupported comp
then case ghcOptNumJobs opts of
NoFlag -> []
Flag n -> ["-j" ++ maybe "" show n]
else []
, [ "-shared" | flagBool ghcOptShared ]
, case flagToMaybe (ghcOptDynLinkMode opts) of
Nothing -> []
Just GhcStaticOnly -> ["-static"]
Just GhcDynamicOnly -> ["-dynamic"]
Just GhcStaticAndDynamic -> ["-static", "-dynamic-too"]
, [ "-fPIC" | flagBool ghcOptFPic ]
, concat [ ["-dylib-install-name", libname] | libname <- flag ghcOptDylibName ]
, concat [ ["-osuf", suf] | suf <- flag ghcOptObjSuffix ]
, concat [ ["-hisuf", suf] | suf <- flag ghcOptHiSuffix ]
, concat [ ["-dynosuf", suf] | suf <- flag ghcOptDynObjSuffix ]
, concat [ ["-dynhisuf",suf] | suf <- flag ghcOptDynHiSuffix ]
, concat [ ["-outputdir", dir] | dir <- flag ghcOptOutputDir
, flagOutputDir implInfo ]
, concat [ ["-odir", dir] | dir <- flag ghcOptObjDir ]
, concat [ ["-hidir", dir] | dir <- flag ghcOptHiDir ]
, concat [ ["-stubdir", dir] | dir <- flag ghcOptStubDir
, flagStubdir implInfo ]
, [ "-i" | flagBool ghcOptSourcePathClear ]
, [ "-i" ++ dir | dir <- flags ghcOptSourcePath ]
, [ "-I" ++ dir | dir <- flags ghcOptCppIncludePath ]
, [ "-optP" ++ opt | opt <- flags ghcOptCppOptions ]
, concat [ [ "-optP-include", "-optP" ++ inc]
| inc <- flags ghcOptCppIncludes ]
, [ "-#include \"" ++ inc ++ "\""
| inc <- flags ghcOptFfiIncludes, flagFfiIncludes implInfo ]
, [ "-optc" ++ opt | opt <- flags ghcOptCcOptions ]
, [ "-optl" ++ opt | opt <- flags ghcOptLinkOptions ]
, ["-l" ++ lib | lib <- flags ghcOptLinkLibs ]
, ["-L" ++ dir | dir <- flags ghcOptLinkLibPath ]
, if isOSX
then concat [ ["-framework", fmwk]
| fmwk <- flags ghcOptLinkFrameworks ]
else []
, if isOSX
then concat [ ["-framework-path", path]
| path <- flags ghcOptLinkFrameworkDirs ]
else []
, [ "-no-hs-main" | flagBool ghcOptLinkNoHsMain ]
, [ "-dynload deploy" | not (null (flags ghcOptRPaths)) ]
, concat [ [ "-optl-Wl,-rpath," ++ dir]
| dir <- flags ghcOptRPaths ]
, concat [ [ case () of
_ | unitIdSupported comp -> "-this-unit-id"
| packageKeySupported comp -> "-this-package-key"
| otherwise -> "-package-name"
, this_arg ]
| this_arg <- flag ghcOptThisUnitId ]
, [ "-hide-all-packages" | flagBool ghcOptHideAllPackages ]
, [ "-no-auto-link-packages" | flagBool ghcOptNoAutoLinkPackages ]
, packageDbArgs implInfo (ghcOptPackageDBs opts)
, concat $ if flagPackageId implInfo
then let space "" = ""
space xs = ' ' : xs
in [ ["-package-id", display ipkgid ++ space (display rns)]
| (ipkgid,_,rns) <- flags ghcOptPackages ]
else [ ["-package", display pkgid]
| (_,pkgid,_) <- flags ghcOptPackages ]
, if supportsHaskell2010 implInfo
then [ "-X" ++ display lang | lang <- flag ghcOptLanguage ]
else []
, [ case M.lookup ext (ghcOptExtensionMap opts) of
Just arg -> arg
Nothing -> error $ "Distribution.Simple.Program.GHC.renderGhcOptions: "
++ display ext ++ " not present in ghcOptExtensionMap."
| ext <- flags ghcOptExtensions ]
, concat [ [ "-ghci-script", script ] | script <- flags ghcOptGHCiScripts
, flagGhciScript implInfo ]
, [ display modu | modu <- flags ghcOptInputModules ]
, flags ghcOptInputFiles
, concat [ [ "-o", out] | out <- flag ghcOptOutputFile ]
, concat [ [ "-dyno", out] | out <- flag ghcOptOutputDynFile ]
, flags ghcOptExtra
]
where
implInfo = getImplInfo comp
isOSX = os == OSX
flag flg = flagToList (flg opts)
flags flg = fromNubListR . flg $ opts
flagBool flg = fromFlagOrDefault False (flg opts)
verbosityOpts :: Verbosity -> [String]
verbosityOpts verbosity
| verbosity >= deafening = ["-v"]
| verbosity >= normal = []
| otherwise = ["-w", "-v0"]
packageDbArgsConf :: PackageDBStack -> [String]
packageDbArgsConf dbstack = case dbstack of
(GlobalPackageDB:UserPackageDB:dbs) -> concatMap specific dbs
(GlobalPackageDB:dbs) -> ("-no-user-package-conf")
: concatMap specific dbs
_ -> ierror
where
specific (SpecificPackageDB db) = [ "-package-conf", db ]
specific _ = ierror
ierror = error $ "internal error: unexpected package db stack: "
++ show dbstack
packageDbArgsDb :: PackageDBStack -> [String]
packageDbArgsDb dbstack = case dbstack of
(GlobalPackageDB:UserPackageDB:dbs)
| all isSpecific dbs -> concatMap single dbs
(GlobalPackageDB:dbs)
| all isSpecific dbs -> "-no-user-package-db"
: concatMap single dbs
dbs -> "-clear-package-db"
: concatMap single dbs
where
single (SpecificPackageDB db) = [ "-package-db", db ]
single GlobalPackageDB = [ "-global-package-db" ]
single UserPackageDB = [ "-user-package-db" ]
isSpecific (SpecificPackageDB _) = True
isSpecific _ = False
packageDbArgs :: GhcImplInfo -> PackageDBStack -> [String]
packageDbArgs implInfo
| flagPackageConf implInfo = packageDbArgsConf
| otherwise = packageDbArgsDb
instance Monoid GhcOptions where
mempty = gmempty
mappend = (Semi.<>)
instance Semigroup GhcOptions where
(<>) = gmappend