module GHC.Driver.Pipeline (
oneShot, compileFile,
preprocess,
compileOne, compileOne',
link,
PhasePlus(..), CompPipeline(..), PipeEnv(..), PipeState(..),
phaseOutputFilename, getOutputFilename, getPipeState, getPipeEnv,
hscPostBackendPhase, getLocation, setModLocation, setDynFlags,
runPhase,
doCpp,
linkingNeeded, checkLinkInfo, writeInterfaceOnlyMode
) where
#include <ghcplatform.h>
#include "HsVersions.h"
import GHC.Prelude
import GHC.Platform
import GHC.Tc.Types
import GHC.Driver.Main
import GHC.Driver.Env hiding ( Hsc )
import GHC.Driver.Errors
import GHC.Driver.Pipeline.Monad
import GHC.Driver.Config
import GHC.Driver.Phases
import GHC.Driver.Session
import GHC.Driver.Backend
import GHC.Driver.Ppr
import GHC.Driver.Hooks
import GHC.Platform.Ways
import GHC.Platform.ArchOS
import GHC.Parser.Header
import GHC.Parser.Errors.Ppr
import GHC.SysTools
import GHC.Utils.TmpFs
import GHC.Linker.ExtraObj
import GHC.Linker.Dynamic
import GHC.Linker.Static
import GHC.Linker.Types
import GHC.Utils.Outputable
import GHC.Utils.Error
import GHC.Utils.Panic
import GHC.Utils.Misc
import GHC.Utils.Monad
import GHC.Utils.Exception as Exception
import GHC.Utils.Logger
import GHC.CmmToLlvm ( llvmFixupAsm, llvmVersionList )
import qualified GHC.LanguageExtensions as LangExt
import GHC.Settings
import GHC.Data.Bag ( unitBag )
import GHC.Data.FastString ( mkFastString )
import GHC.Data.StringBuffer ( hGetStringBuffer, hPutStringBuffer )
import GHC.Data.Maybe ( expectJust )
import GHC.Iface.Make ( mkFullIface )
import GHC.Iface.UpdateIdInfos ( updateModDetailsIdInfos )
import GHC.Types.Basic ( SuccessFlag(..) )
import GHC.Types.Target
import GHC.Types.SrcLoc
import GHC.Types.SourceFile
import GHC.Types.SourceError
import GHC.Unit
import GHC.Unit.Env
import GHC.Unit.State
import GHC.Unit.Finder
import GHC.Unit.Module.ModSummary
import GHC.Unit.Module.ModDetails
import GHC.Unit.Module.ModIface
import GHC.Unit.Module.Graph (needsTemplateHaskellOrQQ)
import GHC.Unit.Module.Deps
import GHC.Unit.Home.ModInfo
import System.Directory
import System.FilePath
import System.IO
import Control.Monad
import qualified Control.Monad.Catch as MC (handle)
import Data.List ( isInfixOf, intercalate )
import Data.Maybe
import Data.Version
import Data.Either ( partitionEithers )
import Data.Time ( UTCTime )
preprocess :: HscEnv
-> FilePath
-> Maybe InputFileBuffer
-> Maybe Phase
-> IO (Either ErrorMessages (DynFlags, FilePath))
preprocess hsc_env input_fn mb_input_buf mb_phase =
handleSourceError (\err -> return (Left (srcErrorMessages err))) $
MC.handle handler $
fmap Right $ do
MASSERT2(isJust mb_phase || isHaskellSrcFilename input_fn, text input_fn)
(dflags, fp, mb_iface) <- runPipeline anyHsc hsc_env (input_fn, mb_input_buf, fmap RealPhase mb_phase)
Nothing
(Temporary TFL_GhcSession)
Nothing
[]
MASSERT(isNothing mb_iface)
return (dflags, fp)
where
srcspan = srcLocSpan $ mkSrcLoc (mkFastString input_fn) 1 1
handler (ProgramError msg) = return $ Left $ unitBag $
mkPlainMsgEnvelope srcspan $ text msg
handler ex = throwGhcExceptionIO ex
compileOne :: HscEnv
-> ModSummary
-> Int
-> Int
-> Maybe ModIface
-> Maybe Linkable
-> SourceModified
-> IO HomeModInfo
compileOne = compileOne' Nothing (Just batchMsg)
compileOne' :: Maybe TcGblEnv
-> Maybe Messager
-> HscEnv
-> ModSummary
-> Int
-> Int
-> Maybe ModIface
-> Maybe Linkable
-> SourceModified
-> IO HomeModInfo
compileOne' m_tc_result mHscMessage
hsc_env0 summary mod_index nmods mb_old_iface mb_old_linkable
source_modified0
= do
let logger = hsc_logger hsc_env0
let tmpfs = hsc_tmpfs hsc_env0
debugTraceMsg logger dflags1 2 (text "compile: input file" <+> text input_fnpp)
(status, plugin_hsc_env) <- hscIncrementalCompile
always_do_basic_recompilation_check
m_tc_result mHscMessage
hsc_env summary source_modified mb_old_iface (mod_index, nmods)
let hsc_env' = plugin_hsc_env
let flags = hsc_dflags hsc_env0
in do unless (gopt Opt_KeepHiFiles flags) $
addFilesToClean tmpfs TFL_CurrentModule $
[ml_hi_file $ ms_location summary]
unless (gopt Opt_KeepOFiles flags) $
addFilesToClean tmpfs TFL_GhcSession $
[ml_obj_file $ ms_location summary]
case (status, bcknd) of
(HscUpToDate iface hmi_details, _) ->
return $! HomeModInfo iface hmi_details mb_old_linkable
(HscNotGeneratingCode iface hmi_details, NoBackend) ->
let mb_linkable = if isHsBootOrSig src_flavour
then Nothing
else Just (LM (ms_hs_date summary) this_mod [])
in return $! HomeModInfo iface hmi_details mb_linkable
(HscNotGeneratingCode _ _, _) -> panic "compileOne HscNotGeneratingCode"
(_, NoBackend) -> panic "compileOne NoBackend"
(HscUpdateBoot iface hmi_details, Interpreter) ->
return $! HomeModInfo iface hmi_details Nothing
(HscUpdateBoot iface hmi_details, _) -> do
touchObjectFile logger dflags object_filename
return $! HomeModInfo iface hmi_details Nothing
(HscUpdateSig iface hmi_details, Interpreter) -> do
let !linkable = LM (ms_hs_date summary) this_mod []
return $! HomeModInfo iface hmi_details (Just linkable)
(HscUpdateSig iface hmi_details, _) -> do
output_fn <- getOutputFilename logger tmpfs next_phase
(Temporary TFL_CurrentModule) basename dflags
next_phase (Just location)
_ <- runPipeline StopLn hsc_env'
(output_fn,
Nothing,
Just (HscOut src_flavour
mod_name (HscUpdateSig iface hmi_details)))
(Just basename)
Persistent
(Just location)
[]
o_time <- getModificationUTCTime object_filename
let !linkable = LM o_time this_mod [DotO object_filename]
return $! HomeModInfo iface hmi_details (Just linkable)
(HscRecomp { hscs_guts = cgguts,
hscs_mod_location = mod_location,
hscs_mod_details = hmi_details,
hscs_partial_iface = partial_iface,
hscs_old_iface_hash = mb_old_iface_hash
}, Interpreter) -> do
final_iface <- mkFullIface hsc_env' partial_iface Nothing
liftIO $ hscMaybeWriteIface logger dflags True final_iface mb_old_iface_hash (ms_location summary)
(hasStub, comp_bc, spt_entries) <- hscInteractive hsc_env' cgguts mod_location
stub_o <- case hasStub of
Nothing -> return []
Just stub_c -> do
stub_o <- compileStub hsc_env' stub_c
return [DotO stub_o]
let hs_unlinked = [BCOs comp_bc spt_entries]
unlinked_time = ms_hs_date summary
let !linkable = LM unlinked_time (ms_mod summary)
(hs_unlinked ++ stub_o)
return $! HomeModInfo final_iface hmi_details (Just linkable)
(HscRecomp{}, _) -> do
output_fn <- getOutputFilename logger tmpfs next_phase
(Temporary TFL_CurrentModule)
basename dflags next_phase (Just location)
(_, _, Just (iface, details)) <- runPipeline StopLn hsc_env'
(output_fn,
Nothing,
Just (HscOut src_flavour mod_name status))
(Just basename)
Persistent
(Just location)
[]
o_time <- getModificationUTCTime object_filename
let !linkable = LM o_time this_mod [DotO object_filename]
return $! HomeModInfo iface details (Just linkable)
where dflags0 = ms_hspp_opts summary
this_mod = ms_mod summary
location = ms_location summary
input_fn = expectJust "compile:hs" (ml_hs_file location)
input_fnpp = ms_hspp_file summary
mod_graph = hsc_mod_graph hsc_env0
needsLinker = needsTemplateHaskellOrQQ mod_graph
isDynWay = any (== WayDyn) (ways dflags0)
isProfWay = any (== WayProf) (ways dflags0)
internalInterpreter = not (gopt Opt_ExternalInterpreter dflags0)
src_flavour = ms_hsc_src summary
mod_name = ms_mod_name summary
next_phase = hscPostBackendPhase src_flavour bcknd
object_filename = ml_obj_file location
dflags1 = if hostIsDynamic && internalInterpreter &&
not isDynWay && not isProfWay && needsLinker
then gopt_set dflags0 Opt_BuildDynamicToo
else dflags0
dflags2 = if not internalInterpreter && needsLinker
then gopt_set dflags1 Opt_ExternalInterpreter
else dflags1
basename = dropExtension input_fn
current_dir = takeDirectory basename
old_paths = includePaths dflags2
loadAsByteCode
| Just (Target _ obj _) <- findTarget summary (hsc_targets hsc_env0)
, not obj
= True
| otherwise = False
(bcknd, dflags3)
| loadAsByteCode
= (Interpreter, dflags2 { backend = Interpreter })
| otherwise
= (backend dflags, dflags2)
dflags = dflags3 { includePaths = addQuoteInclude old_paths [current_dir] }
hsc_env = hsc_env0 {hsc_dflags = dflags}
force_recomp = gopt Opt_ForceRecomp dflags
source_modified
| force_recomp || loadAsByteCode = SourceModified
| otherwise = source_modified0
always_do_basic_recompilation_check = case bcknd of
Interpreter -> True
_ -> False
compileForeign :: HscEnv -> ForeignSrcLang -> FilePath -> IO FilePath
compileForeign _ RawObject object_file = return object_file
compileForeign hsc_env lang stub_c = do
let phase = case lang of
LangC -> Cc
LangCxx -> Ccxx
LangObjc -> Cobjc
LangObjcxx -> Cobjcxx
LangAsm -> As True
#if __GLASGOW_HASKELL__ < 811
RawObject -> panic "compileForeign: should be unreachable"
#endif
(_, stub_o, _) <- runPipeline StopLn hsc_env
(stub_c, Nothing, Just (RealPhase phase))
Nothing (Temporary TFL_GhcSession)
Nothing
[]
return stub_o
compileStub :: HscEnv -> FilePath -> IO FilePath
compileStub hsc_env stub_c = compileForeign hsc_env LangC stub_c
compileEmptyStub :: DynFlags -> HscEnv -> FilePath -> ModLocation -> ModuleName -> IO ()
compileEmptyStub dflags hsc_env basename location mod_name = do
let logger = hsc_logger hsc_env
let tmpfs = hsc_tmpfs hsc_env
empty_stub <- newTempName logger tmpfs dflags TFL_CurrentModule "c"
let home_unit = hsc_home_unit hsc_env
src = text "int" <+> ppr (mkHomeModule home_unit mod_name) <+> text "= 0;"
writeFile empty_stub (showSDoc dflags (pprCode CStyle src))
_ <- runPipeline StopLn hsc_env
(empty_stub, Nothing, Nothing)
(Just basename)
Persistent
(Just location)
[]
return ()
link :: GhcLink
-> Logger
-> TmpFs
-> Hooks
-> DynFlags
-> UnitEnv
-> Bool
-> HomePackageTable
-> IO SuccessFlag
link ghcLink logger tmpfs hooks dflags unit_env batch_attempt_linking hpt =
case linkHook hooks of
Nothing -> case ghcLink of
NoLink -> return Succeeded
LinkBinary -> link' logger tmpfs dflags unit_env batch_attempt_linking hpt
LinkStaticLib -> link' logger tmpfs dflags unit_env batch_attempt_linking hpt
LinkDynLib -> link' logger tmpfs dflags unit_env batch_attempt_linking hpt
LinkInMemory
| platformMisc_ghcWithInterpreter $ platformMisc dflags
->
return Succeeded
| otherwise
-> panicBadLink LinkInMemory
Just h -> h ghcLink dflags batch_attempt_linking hpt
panicBadLink :: GhcLink -> a
panicBadLink other = panic ("link: GHC not built to link this way: " ++
show other)
link' :: Logger
-> TmpFs
-> DynFlags
-> UnitEnv
-> Bool
-> HomePackageTable
-> IO SuccessFlag
link' logger tmpfs dflags unit_env batch_attempt_linking hpt
| batch_attempt_linking
= do
let
staticLink = case ghcLink dflags of
LinkStaticLib -> True
_ -> False
home_mod_infos = eltsHpt hpt
pkg_deps = concatMap (map fst . dep_pkgs . mi_deps . hm_iface) home_mod_infos
linkables = map (expectJust "link".hm_linkable) home_mod_infos
debugTraceMsg logger dflags 3 (text "link: linkables are ..." $$ vcat (map ppr linkables))
if isNoLink (ghcLink dflags)
then do debugTraceMsg logger dflags 3 (text "link(batch): linking omitted (-c flag given).")
return Succeeded
else do
let getOfiles (LM _ _ us) = map nameOfObject (filter isObject us)
obj_files = concatMap getOfiles linkables
platform = targetPlatform dflags
exe_file = exeFileName platform staticLink (outputFile dflags)
linking_needed <- linkingNeeded logger dflags unit_env staticLink linkables pkg_deps
if not (gopt Opt_ForceRecomp dflags) && not linking_needed
then do debugTraceMsg logger dflags 2 (text exe_file <+> text "is up to date, linking not required.")
return Succeeded
else do
compilationProgressMsg logger dflags (text "Linking " <> text exe_file <> text " ...")
let link = case ghcLink dflags of
LinkBinary -> linkBinary logger tmpfs
LinkStaticLib -> linkStaticLib logger
LinkDynLib -> linkDynLibCheck logger tmpfs
other -> panicBadLink other
link dflags unit_env obj_files pkg_deps
debugTraceMsg logger dflags 3 (text "link: done")
return Succeeded
| otherwise
= do debugTraceMsg logger dflags 3 (text "link(batch): upsweep (partially) failed OR" $$
text " Main.main not exported; not linking.")
return Succeeded
linkingNeeded :: Logger -> DynFlags -> UnitEnv -> Bool -> [Linkable] -> [UnitId] -> IO Bool
linkingNeeded logger dflags unit_env staticLink linkables pkg_deps = do
let platform = ue_platform unit_env
unit_state = ue_units unit_env
exe_file = exeFileName platform staticLink (outputFile dflags)
e_exe_time <- tryIO $ getModificationUTCTime exe_file
case e_exe_time of
Left _ -> return True
Right t -> do
let extra_ld_inputs = [ f | FileOption _ f <- ldInputs dflags ]
e_extra_times <- mapM (tryIO . getModificationUTCTime) extra_ld_inputs
let (errs,extra_times) = partitionEithers e_extra_times
let obj_times = map linkableTime linkables ++ extra_times
if not (null errs) || any (t <) obj_times
then return True
else do
let pkg_hslibs = [ (collectLibraryDirs (ways dflags) [c], lib)
| Just c <- map (lookupUnitId unit_state) pkg_deps,
lib <- unitHsLibs (ghcNameVersion dflags) (ways dflags) c ]
pkg_libfiles <- mapM (uncurry (findHSLib platform (ways dflags))) pkg_hslibs
if any isNothing pkg_libfiles then return True else do
e_lib_times <- mapM (tryIO . getModificationUTCTime)
(catMaybes pkg_libfiles)
let (lib_errs,lib_times) = partitionEithers e_lib_times
if not (null lib_errs) || any (t <) lib_times
then return True
else checkLinkInfo logger dflags unit_env pkg_deps exe_file
findHSLib :: Platform -> Ways -> [String] -> String -> IO (Maybe FilePath)
findHSLib platform ws dirs lib = do
let batch_lib_file = if WayDyn `notElem` ws
then "lib" ++ lib <.> "a"
else platformSOName platform lib
found <- filterM doesFileExist (map (</> batch_lib_file) dirs)
case found of
[] -> return Nothing
(x:_) -> return (Just x)
oneShot :: HscEnv -> Phase -> [(String, Maybe Phase)] -> IO ()
oneShot hsc_env stop_phase srcs = do
o_files <- mapM (compileFile hsc_env stop_phase) srcs
doLink hsc_env stop_phase o_files
compileFile :: HscEnv -> Phase -> (FilePath, Maybe Phase) -> IO FilePath
compileFile hsc_env stop_phase (src, mb_phase) = do
exists <- doesFileExist src
when (not exists) $
throwGhcExceptionIO (CmdLineError ("does not exist: " ++ src))
let
dflags = hsc_dflags hsc_env
mb_o_file = outputFile dflags
ghc_link = ghcLink dflags
output
| NoBackend <- backend dflags = Temporary TFL_CurrentModule
| StopLn <- stop_phase, not (isNoLink ghc_link) = Persistent
| isJust mb_o_file = SpecificFile
| otherwise = Persistent
( _, out_file, _) <- runPipeline stop_phase hsc_env
(src, Nothing, fmap RealPhase mb_phase)
Nothing
output
Nothing []
return out_file
doLink :: HscEnv -> Phase -> [FilePath] -> IO ()
doLink hsc_env stop_phase o_files
| not (isStopLn stop_phase)
= return ()
| otherwise
= let
dflags = hsc_dflags hsc_env
logger = hsc_logger hsc_env
unit_env = hsc_unit_env hsc_env
tmpfs = hsc_tmpfs hsc_env
in case ghcLink dflags of
NoLink -> return ()
LinkBinary -> linkBinary logger tmpfs dflags unit_env o_files []
LinkStaticLib -> linkStaticLib logger dflags unit_env o_files []
LinkDynLib -> linkDynLibCheck logger tmpfs dflags unit_env o_files []
other -> panicBadLink other
runPipeline
:: Phase
-> HscEnv
-> (FilePath, Maybe InputFileBuffer, Maybe PhasePlus)
-> Maybe FilePath
-> PipelineOutput
-> Maybe ModLocation
-> [FilePath]
-> IO (DynFlags, FilePath, Maybe (ModIface, ModDetails))
runPipeline stop_phase hsc_env0 (input_fn, mb_input_buf, mb_phase)
mb_basename output maybe_loc foreign_os
= do let
dflags0 = hsc_dflags hsc_env0
dflags = dflags0 { dumpPrefix = Just (basename ++ ".") }
hsc_env = hsc_env0 {hsc_dflags = dflags}
logger = hsc_logger hsc_env
tmpfs = hsc_tmpfs hsc_env
(input_basename, suffix) = splitExtension input_fn
suffix' = drop 1 suffix
basename | Just b <- mb_basename = b
| otherwise = input_basename
start_phase = fromMaybe (RealPhase (startPhase suffix')) mb_phase
isHaskell (RealPhase (Unlit _)) = True
isHaskell (RealPhase (Cpp _)) = True
isHaskell (RealPhase (HsPp _)) = True
isHaskell (RealPhase (Hsc _)) = True
isHaskell (HscOut {}) = True
isHaskell _ = False
isHaskellishFile = isHaskell start_phase
env = PipeEnv{ stop_phase,
src_filename = input_fn,
src_basename = basename,
src_suffix = suffix',
output_spec = output }
when (isBackpackishSuffix suffix') $
throwGhcExceptionIO (UsageError
("use --backpack to process " ++ input_fn))
let happensBefore' = happensBefore (targetPlatform dflags)
case start_phase of
RealPhase start_phase' ->
when (not (start_phase' `happensBefore'` stop_phase ||
start_phase' `eqPhase` stop_phase)) $
throwGhcExceptionIO (UsageError
("cannot compile this file to desired target: "
++ input_fn))
HscOut {} -> return ()
input_fn' <- case (start_phase, mb_input_buf) of
(RealPhase real_start_phase, Just input_buf) -> do
let suffix = phaseInputExt real_start_phase
fn <- newTempName logger tmpfs dflags TFL_CurrentModule suffix
hdl <- openBinaryFile fn WriteMode
hPutStrLn hdl $ "{-# LINE 1 \""++ input_fn ++ "\"#-}"
hPutStringBuffer hdl input_buf
hClose hdl
return fn
(_, _) -> return input_fn
debugTraceMsg logger dflags 4 (text "Running the pipeline")
r <- runPipeline' start_phase hsc_env env input_fn'
maybe_loc foreign_os
let dflags = hsc_dflags hsc_env
when isHaskellishFile $
dynamicTooState dflags >>= \case
DT_Dont -> return ()
DT_Dyn -> return ()
DT_OK -> return ()
DT_Failed
| OSMinGW32 <- platformOS (targetPlatform dflags) -> return ()
| otherwise -> do
debugTraceMsg logger dflags 4
(text "Running the full pipeline again for -dynamic-too")
let dflags' = flip gopt_unset Opt_BuildDynamicToo
$ setDynamicNow
$ dflags
hsc_env' <- newHscEnv dflags'
(dbs,unit_state,home_unit) <- initUnits logger dflags' Nothing
let unit_env = UnitEnv
{ ue_platform = targetPlatform dflags'
, ue_namever = ghcNameVersion dflags'
, ue_home_unit = home_unit
, ue_units = unit_state
}
let hsc_env'' = hsc_env'
{ hsc_unit_env = unit_env
, hsc_unit_dbs = Just dbs
}
_ <- runPipeline' start_phase hsc_env'' env input_fn'
maybe_loc foreign_os
return ()
return r
runPipeline'
:: PhasePlus
-> HscEnv
-> PipeEnv
-> FilePath
-> Maybe ModLocation
-> [FilePath]
-> IO (DynFlags, FilePath, Maybe (ModIface, ModDetails))
runPipeline' start_phase hsc_env env input_fn
maybe_loc foreign_os
= do
let state = PipeState{ hsc_env, maybe_loc, foreign_os = foreign_os, iface = Nothing }
(pipe_state, fp) <- evalP (pipeLoop start_phase input_fn) env state
return (pipeStateDynFlags pipe_state, fp, pipeStateModIface pipe_state)
pipeLoop :: PhasePlus -> FilePath -> CompPipeline FilePath
pipeLoop phase input_fn = do
env <- getPipeEnv
dflags <- getDynFlags
logger <- getLogger
let happensBefore' = happensBefore (targetPlatform dflags)
stopPhase = stop_phase env
case phase of
RealPhase realPhase | realPhase `eqPhase` stopPhase
->
case output_spec env of
Temporary _ ->
return input_fn
output ->
do pst <- getPipeState
tmpfs <- hsc_tmpfs <$> getPipeSession
final_fn <- liftIO $ getOutputFilename logger tmpfs
stopPhase output (src_basename env)
dflags stopPhase (maybe_loc pst)
when (final_fn /= input_fn) $ do
let msg = ("Copying `" ++ input_fn ++"' to `" ++ final_fn ++ "'")
line_prag = Just ("{-# LINE 1 \"" ++ src_filename env ++ "\" #-}\n")
liftIO $ copyWithHeader logger dflags msg line_prag input_fn final_fn
return final_fn
| not (realPhase `happensBefore'` stopPhase)
-> panic ("pipeLoop: at phase " ++ show realPhase ++
" but I wanted to stop at phase " ++ show stopPhase)
_
-> do liftIO $ debugTraceMsg logger dflags 4
(text "Running phase" <+> ppr phase)
case phase of
HscOut {} -> do
let noDynToo = do
(next_phase, output_fn) <- runHookedPhase phase input_fn
pipeLoop next_phase output_fn
let dynToo = do
r <- noDynToo
dynamicTooState dflags >>= \case
DT_OK -> do
let dflags' = setDynamicNow dflags
setDynFlags dflags'
(next_phase, output_fn) <- runHookedPhase phase input_fn
_ <- pipeLoop next_phase output_fn
setDynFlags dflags
return r
_ -> return r
dynamicTooState dflags >>= \case
DT_Dont -> noDynToo
DT_Failed -> noDynToo
DT_OK -> dynToo
DT_Dyn -> noDynToo
_ -> do
(next_phase, output_fn) <- runHookedPhase phase input_fn
pipeLoop next_phase output_fn
runHookedPhase :: PhasePlus -> FilePath -> CompPipeline (PhasePlus, FilePath)
runHookedPhase pp input = do
hooks <- hsc_hooks <$> getPipeSession
case runPhaseHook hooks of
Nothing -> runPhase pp input
Just h -> h pp input
phaseOutputFilename :: Phase -> CompPipeline FilePath
phaseOutputFilename next_phase = do
PipeEnv{stop_phase, src_basename, output_spec} <- getPipeEnv
PipeState{maybe_loc,hsc_env} <- getPipeState
dflags <- getDynFlags
logger <- getLogger
let tmpfs = hsc_tmpfs hsc_env
liftIO $ getOutputFilename logger tmpfs stop_phase output_spec
src_basename dflags next_phase maybe_loc
getOutputFilename
:: Logger
-> TmpFs
-> Phase
-> PipelineOutput
-> String
-> DynFlags
-> Phase
-> Maybe ModLocation
-> IO FilePath
getOutputFilename logger tmpfs stop_phase output basename dflags next_phase maybe_location
| is_last_phase, Persistent <- output = persistent_fn
| is_last_phase, SpecificFile <- output = case outputFile dflags of
Just f -> return f
Nothing ->
panic "SpecificFile: No filename"
| keep_this_output = persistent_fn
| Temporary lifetime <- output = newTempName logger tmpfs dflags lifetime suffix
| otherwise = newTempName logger tmpfs dflags TFL_CurrentModule
suffix
where
hcsuf = hcSuf dflags
odir = objectDir dflags
osuf = objectSuf dflags
keep_hc = gopt Opt_KeepHcFiles dflags
keep_hscpp = gopt Opt_KeepHscppFiles dflags
keep_s = gopt Opt_KeepSFiles dflags
keep_bc = gopt Opt_KeepLlvmFiles dflags
myPhaseInputExt HCc = hcsuf
myPhaseInputExt MergeForeign = osuf
myPhaseInputExt StopLn = osuf
myPhaseInputExt other = phaseInputExt other
is_last_phase = next_phase `eqPhase` stop_phase
keep_this_output =
case next_phase of
As _ | keep_s -> True
LlvmOpt | keep_bc -> True
HCc | keep_hc -> True
HsPp _ | keep_hscpp -> True
_other -> False
suffix = myPhaseInputExt next_phase
persistent_fn
| StopLn <- next_phase = return odir_persistent
| otherwise = return persistent
persistent = basename <.> suffix
odir_persistent
| Just loc <- maybe_location = ml_obj_file loc
| Just d <- odir = d </> persistent
| otherwise = persistent
llvmOptions :: DynFlags
-> [(String, String)]
llvmOptions dflags =
[("-enable-tbaa -tbaa", "-enable-tbaa") | gopt Opt_LlvmTBAA dflags ]
++ [("-relocation-model=" ++ rmodel
,"-relocation-model=" ++ rmodel) | not (null rmodel)]
++ [("-stack-alignment=" ++ (show align)
,"-stack-alignment=" ++ (show align)) | align > 0 ]
++ [("", "-mcpu=" ++ mcpu) | not (null mcpu)
, not (any (isInfixOf "-mcpu") (getOpts dflags opt_lc)) ]
++ [("", "-mattr=" ++ attrs) | not (null attrs) ]
++ [("", "-target-abi=" ++ abi) | not (null abi) ]
where target = platformMisc_llvmTarget $ platformMisc dflags
Just (LlvmTarget _ mcpu mattr) = lookup target (llvmTargets $ llvmConfig dflags)
rmodel | gopt Opt_PIC dflags = "pic"
| positionIndependent dflags = "pic"
| WayDyn `elem` ways dflags = "dynamic-no-pic"
| otherwise = "static"
platform = targetPlatform dflags
align :: Int
align = case platformArch platform of
ArchX86_64 | isAvxEnabled dflags -> 32
_ -> 0
attrs :: String
attrs = intercalate "," $ mattr
++ ["+sse42" | isSse4_2Enabled dflags ]
++ ["+sse2" | isSse2Enabled platform ]
++ ["+sse" | isSseEnabled platform ]
++ ["+avx512f" | isAvx512fEnabled dflags ]
++ ["+avx2" | isAvx2Enabled dflags ]
++ ["+avx" | isAvxEnabled dflags ]
++ ["+avx512cd"| isAvx512cdEnabled dflags ]
++ ["+avx512er"| isAvx512erEnabled dflags ]
++ ["+avx512pf"| isAvx512pfEnabled dflags ]
++ ["+bmi" | isBmiEnabled dflags ]
++ ["+bmi2" | isBmi2Enabled dflags ]
abi :: String
abi = case platformArch (targetPlatform dflags) of
ArchRISCV64 -> "lp64d"
_ -> ""
runPhase :: PhasePlus
-> FilePath
-> CompPipeline (PhasePlus,
FilePath)
runPhase (RealPhase (Unlit sf)) input_fn = do
let
escape ('\\':cs) = '\\':'\\': escape cs
escape ('\"':cs) = '\\':'\"': escape cs
escape ('\'':cs) = '\\':'\'': escape cs
escape (c:cs) = c : escape cs
escape [] = []
output_fn <- phaseOutputFilename (Cpp sf)
let flags = [
GHC.SysTools.Option "-h"
, GHC.SysTools.Option $ escape input_fn
, GHC.SysTools.FileOption "" input_fn
, GHC.SysTools.FileOption "" output_fn
]
dflags <- getDynFlags
logger <- getLogger
liftIO $ GHC.SysTools.runUnlit logger dflags flags
return (RealPhase (Cpp sf), output_fn)
runPhase (RealPhase (Cpp sf)) input_fn
= do
dflags0 <- getDynFlags
logger <- getLogger
src_opts <- liftIO $ getOptionsFromFile dflags0 input_fn
(dflags1, unhandled_flags, warns)
<- liftIO $ parseDynamicFilePragma dflags0 src_opts
setDynFlags dflags1
liftIO $ checkProcessArgsResult unhandled_flags
if not (xopt LangExt.Cpp dflags1) then do
unless (gopt Opt_Pp dflags1) $
liftIO $ handleFlagWarnings logger dflags1 warns
return (RealPhase (HsPp sf), input_fn)
else do
output_fn <- phaseOutputFilename (HsPp sf)
hsc_env <- getPipeSession
liftIO $ doCpp logger
(hsc_tmpfs hsc_env)
(hsc_dflags hsc_env)
(hsc_unit_env hsc_env)
True
input_fn output_fn
src_opts <- liftIO $ getOptionsFromFile dflags0 output_fn
(dflags2, unhandled_flags, warns)
<- liftIO $ parseDynamicFilePragma dflags0 src_opts
liftIO $ checkProcessArgsResult unhandled_flags
unless (gopt Opt_Pp dflags2) $
liftIO $ handleFlagWarnings logger dflags2 warns
setDynFlags dflags2
return (RealPhase (HsPp sf), output_fn)
runPhase (RealPhase (HsPp sf)) input_fn = do
dflags <- getDynFlags
logger <- getLogger
if not (gopt Opt_Pp dflags) then
return (RealPhase (Hsc sf), input_fn)
else do
PipeEnv{src_basename, src_suffix} <- getPipeEnv
let orig_fn = src_basename <.> src_suffix
output_fn <- phaseOutputFilename (Hsc sf)
liftIO $ GHC.SysTools.runPp logger dflags
( [ GHC.SysTools.Option orig_fn
, GHC.SysTools.Option input_fn
, GHC.SysTools.FileOption "" output_fn
]
)
src_opts <- liftIO $ getOptionsFromFile dflags output_fn
(dflags1, unhandled_flags, warns)
<- liftIO $ parseDynamicFilePragma dflags src_opts
setDynFlags dflags1
liftIO $ checkProcessArgsResult unhandled_flags
liftIO $ handleFlagWarnings logger dflags1 warns
return (RealPhase (Hsc sf), output_fn)
runPhase (RealPhase (Hsc src_flavour)) input_fn
= do
dflags0 <- getDynFlags
PipeEnv{ stop_phase=stop,
src_basename=basename,
src_suffix=suff } <- getPipeEnv
let current_dir = takeDirectory basename
new_includes = addQuoteInclude paths [current_dir]
paths = includePaths dflags0
dflags = dflags0 { includePaths = new_includes }
setDynFlags dflags
(hspp_buf,mod_name,imps,src_imps) <- liftIO $ do
buf <- hGetStringBuffer input_fn
let imp_prelude = xopt LangExt.ImplicitPrelude dflags
popts = initParserOpts dflags
eimps <- getImports popts imp_prelude buf input_fn (basename <.> suff)
case eimps of
Left errs -> throwErrors (fmap pprError errs)
Right (src_imps,imps,L _ mod_name) -> return
(Just buf, mod_name, imps, src_imps)
location <- getLocation src_flavour mod_name
let o_file = ml_obj_file location
hi_file = ml_hi_file location
hie_file = ml_hie_file location
dest_file | writeInterfaceOnlyMode dflags
= hi_file
| otherwise
= o_file
src_timestamp <- liftIO $ getModificationUTCTime (basename <.> suff)
source_unchanged <- liftIO $
if not (isStopLn stop)
then return SourceModified
else do dest_file_mod <- sourceModified dest_file src_timestamp
hie_file_mod <- if gopt Opt_WriteHie dflags
then sourceModified hie_file
src_timestamp
else pure False
if dest_file_mod || hie_file_mod
then return SourceModified
else return SourceUnmodified
PipeState{hsc_env=hsc_env'} <- getPipeState
mod <- liftIO $ addHomeModuleToFinder hsc_env' mod_name location
let
mod_summary = ModSummary { ms_mod = mod,
ms_hsc_src = src_flavour,
ms_hspp_file = input_fn,
ms_hspp_opts = dflags,
ms_hspp_buf = hspp_buf,
ms_location = location,
ms_hs_date = src_timestamp,
ms_obj_date = Nothing,
ms_parsed_mod = Nothing,
ms_iface_date = Nothing,
ms_hie_date = Nothing,
ms_textual_imps = imps,
ms_srcimps = src_imps }
let msg hsc_env _ what _ = oneShotMsg hsc_env what
(result, plugin_hsc_env) <-
liftIO $ hscIncrementalCompile True Nothing (Just msg) hsc_env'
mod_summary source_unchanged Nothing (1,1)
setPlugins (hsc_plugins plugin_hsc_env)
(hsc_static_plugins plugin_hsc_env)
setDynFlags (hsc_dflags plugin_hsc_env)
return (HscOut src_flavour mod_name result,
panic "HscOut doesn't have an input filename")
runPhase (HscOut src_flavour mod_name result) _ = do
dflags <- getDynFlags
logger <- getLogger
location <- getLocation src_flavour mod_name
setModLocation location
let o_file = ml_obj_file location
next_phase = hscPostBackendPhase src_flavour (backend dflags)
case result of
HscNotGeneratingCode _ _ ->
return (RealPhase StopLn,
panic "No output filename from Hsc when no-code")
HscUpToDate _ _ ->
do liftIO $ touchObjectFile logger dflags o_file
return (RealPhase StopLn, o_file)
HscUpdateBoot _ _ ->
do
liftIO $ touchObjectFile logger dflags o_file
return (RealPhase StopLn, o_file)
HscUpdateSig _ _ ->
do
PipeState{hsc_env=hsc_env'} <- getPipeState
let input_fn = expectJust "runPhase" (ml_hs_file location)
basename = dropExtension input_fn
liftIO $ compileEmptyStub dflags hsc_env' basename location mod_name
return (RealPhase StopLn, o_file)
HscRecomp { hscs_guts = cgguts,
hscs_mod_location = mod_location,
hscs_mod_details = mod_details,
hscs_partial_iface = partial_iface,
hscs_old_iface_hash = mb_old_iface_hash
}
-> do output_fn <- phaseOutputFilename next_phase
PipeState{hsc_env=hsc_env'} <- getPipeState
(outputFilename, mStub, foreign_files, cg_infos) <- liftIO $
hscGenHardCode hsc_env' cgguts mod_location output_fn
let dflags = hsc_dflags hsc_env'
final_iface <- liftIO (mkFullIface hsc_env' partial_iface (Just cg_infos))
let final_mod_details
| gopt Opt_OmitInterfacePragmas dflags
= mod_details
| otherwise =
updateModDetailsIdInfos cg_infos mod_details
setIface final_iface final_mod_details
liftIO $ hscMaybeWriteIface logger dflags False final_iface mb_old_iface_hash mod_location
stub_o <- liftIO (mapM (compileStub hsc_env') mStub)
foreign_os <- liftIO $
mapM (uncurry (compileForeign hsc_env')) foreign_files
setForeignOs (maybe [] return stub_o ++ foreign_os)
return (RealPhase next_phase, outputFilename)
runPhase (RealPhase CmmCpp) input_fn = do
hsc_env <- getPipeSession
logger <- getLogger
output_fn <- phaseOutputFilename Cmm
liftIO $ doCpp logger
(hsc_tmpfs hsc_env)
(hsc_dflags hsc_env)
(hsc_unit_env hsc_env)
False
input_fn output_fn
return (RealPhase Cmm, output_fn)
runPhase (RealPhase Cmm) input_fn = do
hsc_env <- getPipeSession
let dflags = hsc_dflags hsc_env
let next_phase = hscPostBackendPhase HsSrcFile (backend dflags)
output_fn <- phaseOutputFilename next_phase
PipeState{hsc_env} <- getPipeState
mstub <- liftIO $ hscCompileCmmFile hsc_env input_fn output_fn
stub_o <- liftIO (mapM (compileStub hsc_env) mstub)
setForeignOs (maybeToList stub_o)
return (RealPhase next_phase, output_fn)
runPhase (RealPhase cc_phase) input_fn
| any (cc_phase `eqPhase`) [Cc, Ccxx, HCc, Cobjc, Cobjcxx]
= do
hsc_env <- getPipeSession
let dflags = hsc_dflags hsc_env
let unit_env = hsc_unit_env hsc_env
let home_unit = hsc_home_unit hsc_env
let tmpfs = hsc_tmpfs hsc_env
let platform = ue_platform unit_env
let hcc = cc_phase `eqPhase` HCc
let cmdline_include_paths = includePaths dflags
pkgs <- if hcc then liftIO $ getHCFilePackages input_fn else return []
ps <- liftIO $ mayThrowUnitErr (preloadUnitsInfo' unit_env pkgs)
let pkg_include_dirs = collectIncludeDirs ps
let include_paths_global = foldr (\ x xs -> ("-I" ++ x) : xs) []
(includePathsGlobal cmdline_include_paths ++ pkg_include_dirs)
let include_paths_quote = foldr (\ x xs -> ("-iquote" ++ x) : xs) []
(includePathsQuote cmdline_include_paths)
let include_paths = include_paths_quote ++ include_paths_global
let more_preprocessor_opts = concat
[ ["-Xpreprocessor", i]
| not hcc
, i <- getOpts dflags opt_P
]
let gcc_extra_viac_flags = extraGccViaCFlags dflags
let pic_c_flags = picCCOpts dflags
let verbFlags = getVerbFlags dflags
let pkg_extra_cc_opts
| hcc = []
| otherwise = collectExtraCcOpts ps
let framework_paths
| platformUsesFrameworks platform
= let pkgFrameworkPaths = collectFrameworksDirs ps
cmdlineFrameworkPaths = frameworkPaths dflags
in map ("-F"++) (cmdlineFrameworkPaths ++ pkgFrameworkPaths)
| otherwise
= []
let cc_opt | optLevel dflags >= 2 = [ "-O2" ]
| optLevel dflags >= 1 = [ "-O" ]
| otherwise = []
let next_phase = As False
output_fn <- phaseOutputFilename next_phase
let
more_hcc_opts =
(if platformArch platform == ArchX86 &&
not (gopt Opt_ExcessPrecision dflags)
then [ "-ffloat-store" ]
else []) ++
["-fno-strict-aliasing"]
ghcVersionH <- liftIO $ getGhcVersionPathName dflags unit_env
logger <- getLogger
liftIO $ GHC.SysTools.runCc (phaseForeignLanguage cc_phase) logger tmpfs dflags (
[ GHC.SysTools.FileOption "" input_fn
, GHC.SysTools.Option "-o"
, GHC.SysTools.FileOption "" output_fn
]
++ map GHC.SysTools.Option (
pic_c_flags
++ (if platformOS platform == OSMinGW32 &&
isHomeUnitId home_unit baseUnitId
then [ "-DCOMPILING_BASE_PACKAGE" ]
else [])
++ (if platformArch platform == ArchSPARC
then ["-mcpu=v9"]
else [])
++ (if (cc_phase /= Ccxx && cc_phase /= Cobjcxx)
then ["-Wimplicit"]
else [])
++ (if hcc
then gcc_extra_viac_flags ++ more_hcc_opts
else [])
++ verbFlags
++ [ "-S" ]
++ cc_opt
++ [ "-include", ghcVersionH ]
++ framework_paths
++ include_paths
++ more_preprocessor_opts
++ pkg_extra_cc_opts
))
return (RealPhase next_phase, output_fn)
runPhase (RealPhase (As with_cpp)) input_fn
= do
hsc_env <- getPipeSession
let dflags = hsc_dflags hsc_env
let logger = hsc_logger hsc_env
let unit_env = hsc_unit_env hsc_env
let platform = ue_platform unit_env
let as_prog | backend dflags == LLVM
, platformOS platform == OSDarwin
= GHC.SysTools.runClang
| otherwise
= GHC.SysTools.runAs
let cmdline_include_paths = includePaths dflags
let pic_c_flags = picCCOpts dflags
next_phase <- maybeMergeForeign
output_fn <- phaseOutputFilename next_phase
liftIO $ createDirectoryIfMissing True (takeDirectory output_fn)
ccInfo <- liftIO $ getCompilerInfo logger dflags
let global_includes = [ GHC.SysTools.Option ("-I" ++ p)
| p <- includePathsGlobal cmdline_include_paths ]
let local_includes = [ GHC.SysTools.Option ("-iquote" ++ p)
| p <- includePathsQuote cmdline_include_paths ]
let runAssembler inputFilename outputFilename
= liftIO $
withAtomicRename outputFilename $ \temp_outputFilename ->
as_prog
logger dflags
(local_includes ++ global_includes
++ map GHC.SysTools.Option pic_c_flags
++ [ GHC.SysTools.Option "-Wa,-mbig-obj"
| platformOS (targetPlatform dflags) == OSMinGW32
, not $ target32Bit (targetPlatform dflags)
]
++ (if platformArch (targetPlatform dflags) == ArchSPARC
then [GHC.SysTools.Option "-mcpu=v9"]
else [])
++ (if any (ccInfo ==) [Clang, AppleClang, AppleClang51]
then [GHC.SysTools.Option "-Qunused-arguments"]
else [])
++ [ GHC.SysTools.Option "-x"
, if with_cpp
then GHC.SysTools.Option "assembler-with-cpp"
else GHC.SysTools.Option "assembler"
, GHC.SysTools.Option "-c"
, GHC.SysTools.FileOption "" inputFilename
, GHC.SysTools.Option "-o"
, GHC.SysTools.FileOption "" temp_outputFilename
])
liftIO $ debugTraceMsg logger dflags 4 (text "Running the assembler")
runAssembler input_fn output_fn
return (RealPhase next_phase, output_fn)
runPhase (RealPhase LlvmOpt) input_fn = do
dflags <- getDynFlags
logger <- getLogger
let
optIdx = max 0 $ min 2 $ optLevel dflags
llvmOpts = case lookup optIdx $ llvmPasses $ llvmConfig dflags of
Just passes -> passes
Nothing -> panic ("runPhase LlvmOpt: llvm-passes file "
++ "is missing passes for level "
++ show optIdx)
defaultOptions = map GHC.SysTools.Option . concat . fmap words . fst
$ unzip (llvmOptions dflags)
optFlag = if null (getOpts dflags opt_lo)
then map GHC.SysTools.Option $ words llvmOpts
else []
output_fn <- phaseOutputFilename LlvmLlc
liftIO $ GHC.SysTools.runLlvmOpt logger dflags
( optFlag
++ defaultOptions ++
[ GHC.SysTools.FileOption "" input_fn
, GHC.SysTools.Option "-o"
, GHC.SysTools.FileOption "" output_fn]
)
return (RealPhase LlvmLlc, output_fn)
runPhase (RealPhase LlvmLlc) input_fn = do
dflags <- getDynFlags
logger <- getLogger
let
llvmOpts = case optLevel dflags of
0 -> "-O1"
1 -> "-O1"
_ -> "-O2"
defaultOptions = map GHC.SysTools.Option . concatMap words . snd
$ unzip (llvmOptions dflags)
optFlag = if null (getOpts dflags opt_lc)
then map GHC.SysTools.Option $ words llvmOpts
else []
next_phase <- if
| gopt Opt_NoLlvmMangler dflags -> return (As False)
| otherwise -> return LlvmMangle
output_fn <- phaseOutputFilename next_phase
liftIO $ GHC.SysTools.runLlvmLlc logger dflags
( optFlag
++ defaultOptions
++ [ GHC.SysTools.FileOption "" input_fn
, GHC.SysTools.Option "-o"
, GHC.SysTools.FileOption "" output_fn
]
)
return (RealPhase next_phase, output_fn)
runPhase (RealPhase LlvmMangle) input_fn = do
let next_phase = As False
output_fn <- phaseOutputFilename next_phase
dflags <- getDynFlags
logger <- getLogger
liftIO $ llvmFixupAsm logger dflags input_fn output_fn
return (RealPhase next_phase, output_fn)
runPhase (RealPhase MergeForeign) input_fn = do
PipeState{foreign_os,hsc_env} <- getPipeState
output_fn <- phaseOutputFilename StopLn
liftIO $ createDirectoryIfMissing True (takeDirectory output_fn)
if null foreign_os
then panic "runPhase(MergeForeign): no foreign objects"
else do
dflags <- getDynFlags
logger <- getLogger
let tmpfs = hsc_tmpfs hsc_env
liftIO $ joinObjectFiles logger tmpfs dflags (input_fn : foreign_os) output_fn
return (RealPhase StopLn, output_fn)
runPhase (RealPhase other) _input_fn =
panic ("runPhase: don't know how to run phase " ++ show other)
maybeMergeForeign :: CompPipeline Phase
maybeMergeForeign
= do
PipeState{foreign_os} <- getPipeState
if null foreign_os then return StopLn else return MergeForeign
getLocation :: HscSource -> ModuleName -> CompPipeline ModLocation
getLocation src_flavour mod_name = do
dflags <- getDynFlags
PipeEnv{ src_basename=basename,
src_suffix=suff } <- getPipeEnv
PipeState { maybe_loc=maybe_loc} <- getPipeState
case maybe_loc of
Just l -> return $ l
{ ml_hs_file = Just $ basename <.> suff
, ml_hi_file = ml_hi_file l -<.> hiSuf dflags
, ml_obj_file = ml_obj_file l -<.> objectSuf dflags
}
_ -> do
location1 <- liftIO $ mkHomeModLocation2 dflags mod_name basename suff
let location2
| HsBootFile <- src_flavour = addBootSuffixLocnOut location1
| otherwise = location1
let ohi = outputHi dflags
location3 | Just fn <- ohi = location2{ ml_hi_file = fn }
| otherwise = location2
let expl_o_file = outputFile dflags
location4 | Just ofile <- expl_o_file
, isNoLink (ghcLink dflags)
= location3 { ml_obj_file = ofile }
| otherwise = location3
return location4
getHCFilePackages :: FilePath -> IO [UnitId]
getHCFilePackages filename =
Exception.bracket (openFile filename ReadMode) hClose $ \h -> do
l <- hGetLine h
case l of
'/':'*':' ':'G':'H':'C':'_':'P':'A':'C':'K':'A':'G':'E':'S':rest ->
return (map stringToUnitId (words rest))
_other ->
return []
linkDynLibCheck :: Logger -> TmpFs -> DynFlags -> UnitEnv -> [String] -> [UnitId] -> IO ()
linkDynLibCheck logger tmpfs dflags unit_env o_files dep_units = do
when (haveRtsOptsFlags dflags) $
putLogMsg logger dflags NoReason SevInfo noSrcSpan
$ withPprStyle defaultUserStyle
(text "Warning: -rtsopts and -with-rtsopts have no effect with -shared." $$
text " Call hs_init_ghc() from your main() function to set these options.")
linkDynLib logger tmpfs dflags unit_env o_files dep_units
doCpp :: Logger -> TmpFs -> DynFlags -> UnitEnv -> Bool -> FilePath -> FilePath -> IO ()
doCpp logger tmpfs dflags unit_env raw input_fn output_fn = do
let hscpp_opts = picPOpts dflags
let cmdline_include_paths = includePaths dflags
let unit_state = ue_units unit_env
pkg_include_dirs <- mayThrowUnitErr
(collectIncludeDirs <$> preloadUnitsInfo unit_env)
let include_paths_global = foldr (\ x xs -> ("-I" ++ x) : xs) []
(includePathsGlobal cmdline_include_paths ++ pkg_include_dirs)
let include_paths_quote = foldr (\ x xs -> ("-iquote" ++ x) : xs) []
(includePathsQuote cmdline_include_paths)
let include_paths = include_paths_quote ++ include_paths_global
let verbFlags = getVerbFlags dflags
let cpp_prog args | raw = GHC.SysTools.runCpp logger dflags args
| otherwise = GHC.SysTools.runCc Nothing logger tmpfs dflags
(GHC.SysTools.Option "-E" : args)
let platform = targetPlatform dflags
targetArch = stringEncodeArch $ platformArch platform
targetOS = stringEncodeOS $ platformOS platform
isWindows = platformOS platform == OSMinGW32
let target_defs =
[ "-D" ++ HOST_OS ++ "_BUILD_OS",
"-D" ++ HOST_ARCH ++ "_BUILD_ARCH",
"-D" ++ targetOS ++ "_HOST_OS",
"-D" ++ targetArch ++ "_HOST_ARCH" ]
let io_manager_defs =
[ "-D__IO_MANAGER_WINIO__=1" | isWindows ] ++
[ "-D__IO_MANAGER_MIO__=1" ]
let sse_defs =
[ "-D__SSE__" | isSseEnabled platform ] ++
[ "-D__SSE2__" | isSse2Enabled platform ] ++
[ "-D__SSE4_2__" | isSse4_2Enabled dflags ]
let avx_defs =
[ "-D__AVX__" | isAvxEnabled dflags ] ++
[ "-D__AVX2__" | isAvx2Enabled dflags ] ++
[ "-D__AVX512CD__" | isAvx512cdEnabled dflags ] ++
[ "-D__AVX512ER__" | isAvx512erEnabled dflags ] ++
[ "-D__AVX512F__" | isAvx512fEnabled dflags ] ++
[ "-D__AVX512PF__" | isAvx512pfEnabled dflags ]
backend_defs <- getBackendDefs logger dflags
let th_defs = [ "-D__GLASGOW_HASKELL_TH__" ]
ghcVersionH <- getGhcVersionPathName dflags unit_env
let hsSourceCppOpts = [ "-include", ghcVersionH ]
let uids = explicitUnits unit_state
pkgs = catMaybes (map (lookupUnit unit_state) uids)
mb_macro_include <-
if not (null pkgs) && gopt Opt_VersionMacros dflags
then do macro_stub <- newTempName logger tmpfs dflags TFL_CurrentModule "h"
writeFile macro_stub (generatePackageVersionMacros pkgs)
return [GHC.SysTools.FileOption "-include" macro_stub]
else return []
cpp_prog ( map GHC.SysTools.Option verbFlags
++ map GHC.SysTools.Option include_paths
++ map GHC.SysTools.Option hsSourceCppOpts
++ map GHC.SysTools.Option target_defs
++ map GHC.SysTools.Option backend_defs
++ map GHC.SysTools.Option th_defs
++ map GHC.SysTools.Option hscpp_opts
++ map GHC.SysTools.Option sse_defs
++ map GHC.SysTools.Option avx_defs
++ map GHC.SysTools.Option io_manager_defs
++ mb_macro_include
++ [ GHC.SysTools.Option "-x"
, GHC.SysTools.Option "assembler-with-cpp"
, GHC.SysTools.Option input_fn
, GHC.SysTools.Option "-o"
, GHC.SysTools.FileOption "" output_fn
])
getBackendDefs :: Logger -> DynFlags -> IO [String]
getBackendDefs logger dflags | backend dflags == LLVM = do
llvmVer <- figureLlvmVersion logger dflags
return $ case fmap llvmVersionList llvmVer of
Just [m] -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,0) ]
Just (m:n:_) -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,n) ]
_ -> []
where
format (major, minor)
| minor >= 100 = error "getBackendDefs: Unsupported minor version"
| otherwise = show $ (100 * major + minor :: Int)
getBackendDefs _ _ =
return []
generatePackageVersionMacros :: [UnitInfo] -> String
generatePackageVersionMacros pkgs = concat
[ generateMacros "" pkgname version
| pkg <- pkgs
, let version = unitPackageVersion pkg
pkgname = map fixchar (unitPackageNameString pkg)
]
fixchar :: Char -> Char
fixchar '-' = '_'
fixchar c = c
generateMacros :: String -> String -> Version -> String
generateMacros prefix name version =
concat
["#define ", prefix, "VERSION_",name," ",show (showVersion version),"\n"
,"#define MIN_", prefix, "VERSION_",name,"(major1,major2,minor) (\\\n"
," (major1) < ",major1," || \\\n"
," (major1) == ",major1," && (major2) < ",major2," || \\\n"
," (major1) == ",major1," && (major2) == ",major2," && (minor) <= ",minor,")"
,"\n\n"
]
where
(major1:major2:minor:_) = map show (versionBranch version ++ repeat 0)
joinObjectFiles :: Logger -> TmpFs -> DynFlags -> [FilePath] -> FilePath -> IO ()
joinObjectFiles logger tmpfs dflags o_files output_fn = do
let toolSettings' = toolSettings dflags
ldIsGnuLd = toolSettings_ldIsGnuLd toolSettings'
osInfo = platformOS (targetPlatform dflags)
ld_r args = GHC.SysTools.runMergeObjects logger tmpfs dflags (
concat
[ [GHC.SysTools.Option "--oformat", GHC.SysTools.Option "pe-bigobj-x86-64"]
| OSMinGW32 == osInfo
, not $ target32Bit (targetPlatform dflags)
]
++ map GHC.SysTools.Option ld_build_id
++ [ GHC.SysTools.Option "-o",
GHC.SysTools.FileOption "" output_fn ]
++ args)
ld_build_id | toolSettings_ldSupportsBuildId toolSettings' = ["--build-id=none"]
| otherwise = []
if ldIsGnuLd
then do
script <- newTempName logger tmpfs dflags TFL_CurrentModule "ldscript"
cwd <- getCurrentDirectory
let o_files_abs = map (\x -> "\"" ++ (cwd </> x) ++ "\"") o_files
writeFile script $ "INPUT(" ++ unwords o_files_abs ++ ")"
ld_r [GHC.SysTools.FileOption "" script]
else if toolSettings_ldSupportsFilelist toolSettings'
then do
filelist <- newTempName logger tmpfs dflags TFL_CurrentModule "filelist"
writeFile filelist $ unlines o_files
ld_r [GHC.SysTools.Option "-filelist",
GHC.SysTools.FileOption "" filelist]
else
ld_r (map (GHC.SysTools.FileOption "") o_files)
writeInterfaceOnlyMode :: DynFlags -> Bool
writeInterfaceOnlyMode dflags =
gopt Opt_WriteInterface dflags &&
NoBackend == backend dflags
sourceModified :: FilePath
-> UTCTime
-> IO Bool
sourceModified dest_file src_timestamp = do
dest_file_exists <- doesFileExist dest_file
if not dest_file_exists
then return True
else do t2 <- getModificationUTCTime dest_file
return (t2 <= src_timestamp)
hscPostBackendPhase :: HscSource -> Backend -> Phase
hscPostBackendPhase HsBootFile _ = StopLn
hscPostBackendPhase HsigFile _ = StopLn
hscPostBackendPhase _ bcknd =
case bcknd of
ViaC -> HCc
NCG -> As False
LLVM -> LlvmOpt
NoBackend -> StopLn
Interpreter -> StopLn
touchObjectFile :: Logger -> DynFlags -> FilePath -> IO ()
touchObjectFile logger dflags path = do
createDirectoryIfMissing True $ takeDirectory path
GHC.SysTools.touch logger dflags "Touching object file" path
getGhcVersionPathName :: DynFlags -> UnitEnv -> IO FilePath
getGhcVersionPathName dflags unit_env = do
candidates <- case ghcVersionFile dflags of
Just path -> return [path]
Nothing -> do
ps <- mayThrowUnitErr (preloadUnitsInfo' unit_env [rtsUnitId])
return ((</> "ghcversion.h") <$> collectIncludeDirs ps)
found <- filterM doesFileExist candidates
case found of
[] -> throwGhcExceptionIO (InstallationError
("ghcversion.h missing; tried: "
++ intercalate ", " candidates))
(x:_) -> return x