{-# OPTIONS_GHC -fspec-constr-threshold=10000 #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE LambdaCase #-}
module GHC.Linker.Deps
( LinkDepsOpts (..)
, LinkDeps (..)
, getLinkDeps
)
where
import GHC.Prelude
import GHC.Platform.Ways
import GHC.Runtime.Interpreter
import GHC.Linker.Types
import GHC.Types.SrcLoc
import GHC.Types.Unique.DSet
import GHC.Types.Unique.DFM
import GHC.Utils.Outputable
import GHC.Utils.Panic
import GHC.Utils.Error
import GHC.Unit.Env
import GHC.Unit.Finder
import GHC.Unit.Module
import GHC.Unit.Module.WholeCoreBindings
import GHC.Unit.Home.ModInfo
import GHC.Iface.Errors.Types
import GHC.Utils.Misc
import qualified GHC.Unit.Home.Graph as HUG
import GHC.Data.Maybe
import Control.Applicative
import Data.List (isSuffixOf)
import System.FilePath
import System.Directory
data LinkDepsOpts = LinkDepsOpts
{ LinkDepsOpts -> String
ldObjSuffix :: !String
, LinkDepsOpts -> Bool
ldForceDyn :: !Bool
, LinkDepsOpts -> UnitEnv
ldUnitEnv :: !UnitEnv
, LinkDepsOpts -> SDocContext
ldPprOpts :: !SDocContext
, LinkDepsOpts -> Bool
ldUseByteCode :: !Bool
, LinkDepsOpts -> DiagnosticOpts IfaceMessage
ldMsgOpts :: !(DiagnosticOpts IfaceMessage)
, LinkDepsOpts -> Ways
ldWays :: !Ways
, LinkDepsOpts -> FinderCache
ldFinderCache :: !FinderCache
, LinkDepsOpts -> FinderOpts
ldFinderOpts :: !FinderOpts
, LinkDepsOpts -> Module -> IO (Maybe Linkable)
ldLoadByteCode :: !(Module -> IO (Maybe Linkable))
, LinkDepsOpts -> [Module] -> IO ([Module], UniqDSet UnitId)
ldGetDependencies :: !([Module] -> IO ([Module], UniqDSet UnitId))
}
data LinkDeps = LinkDeps
{ LinkDeps -> [Linkable]
ldNeededLinkables :: [Linkable]
, LinkDeps -> [Linkable]
ldAllLinkables :: [Linkable]
, LinkDeps -> [UnitId]
ldUnits :: [UnitId]
, LinkDeps -> UniqDSet UnitId
ldNeededUnits :: UniqDSet UnitId
}
getLinkDeps
:: LinkDepsOpts
-> Interp
-> LoaderState
-> SrcSpan
-> [Module]
-> IO LinkDeps
getLinkDeps :: LinkDepsOpts
-> Interp -> LoaderState -> SrcSpan -> [Module] -> IO LinkDeps
getLinkDeps LinkDepsOpts
opts Interp
interp LoaderState
pls SrcSpan
span [Module]
mods = do
maybe_normal_osuf <- LinkDepsOpts -> Interp -> SrcSpan -> IO (Maybe String)
checkNonStdWay LinkDepsOpts
opts Interp
interp SrcSpan
span
get_link_deps opts pls maybe_normal_osuf span mods
get_link_deps
:: LinkDepsOpts
-> LoaderState
-> Maybe FilePath
-> SrcSpan
-> [Module]
-> IO LinkDeps
get_link_deps :: LinkDepsOpts
-> LoaderState
-> Maybe String
-> SrcSpan
-> [Module]
-> IO LinkDeps
get_link_deps LinkDepsOpts
opts LoaderState
pls Maybe String
maybe_normal_osuf SrcSpan
span [Module]
mods = do
(mods_s, pkgs_s) <- LinkDepsOpts -> [Module] -> IO ([Module], UniqDSet UnitId)
ldGetDependencies LinkDepsOpts
opts [Module]
relevant_mods
let
(mods_needed, links_got) = partitionWith split_mods mods_s
pkgs_needed = UniqDFM UnitId UnitId -> [UnitId]
forall {k} (key :: k) elt. UniqDFM key elt -> [elt]
eltsUDFM (UniqDFM UnitId UnitId -> [UnitId])
-> UniqDFM UnitId UnitId -> [UnitId]
forall a b. (a -> b) -> a -> b
$ UniqDSet UnitId -> UniqDFM UnitId UnitId
forall a. UniqDSet a -> UniqDFM a a
getUniqDSet UniqDSet UnitId
pkgs_s UniqDFM UnitId UnitId
-> UniqDFM UnitId LoadedPkgInfo -> UniqDFM UnitId UnitId
forall {k} (key :: k) elt1 elt2.
UniqDFM key elt1 -> UniqDFM key elt2 -> UniqDFM key elt1
`minusUDFM` LoaderState -> UniqDFM UnitId LoadedPkgInfo
pkgs_loaded LoaderState
pls
split_mods Module
mod =
let is_linked :: Maybe Linkable
is_linked = ModuleEnv Linkable -> Module -> Maybe Linkable
forall a. ModuleEnv a -> Module -> Maybe a
lookupModuleEnv (LoaderState -> ModuleEnv Linkable
objs_loaded LoaderState
pls) Module
mod
Maybe Linkable -> Maybe Linkable -> Maybe Linkable
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ModuleEnv Linkable -> Module -> Maybe Linkable
forall a. ModuleEnv a -> Module -> Maybe a
lookupModuleEnv (LoaderState -> ModuleEnv Linkable
bcos_loaded LoaderState
pls) Module
mod
in case Maybe Linkable
is_linked of
Just Linkable
linkable -> Linkable -> Either Module Linkable
forall a b. b -> Either a b
Right Linkable
linkable
Maybe Linkable
Nothing -> Module -> Either Module Linkable
forall a b. a -> Either a b
Left Module
mod
lnks_needed <- mapM (get_linkable (ldObjSuffix opts)) mods_needed
return $ LinkDeps
{ ldNeededLinkables = lnks_needed
, ldAllLinkables = links_got ++ lnks_needed
, ldUnits = pkgs_needed
, ldNeededUnits = pkgs_s
}
where
unit_env :: UnitEnv
unit_env = LinkDepsOpts -> UnitEnv
ldUnitEnv LinkDepsOpts
opts
relevant_mods :: [Module]
relevant_mods = (Module -> Bool) -> [Module] -> [Module]
forall a. (a -> Bool) -> [a] -> [a]
filterOut Module -> Bool
isInteractiveModule [Module]
mods
no_obj :: Outputable a => a -> IO b
no_obj :: forall a b. Outputable a => a -> IO b
no_obj a
mod = LinkDepsOpts -> SrcSpan -> SDoc -> IO b
forall a. LinkDepsOpts -> SrcSpan -> SDoc -> IO a
dieWith LinkDepsOpts
opts SrcSpan
span (SDoc -> IO b) -> SDoc -> IO b
forall a b. (a -> b) -> a -> b
$
String -> SDoc
forall doc. IsLine doc => String -> doc
text String
"cannot find object file for module " SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<>
SDoc -> SDoc
quotes (a -> SDoc
forall a. Outputable a => a -> SDoc
ppr a
mod) SDoc -> SDoc -> SDoc
forall doc. IsDoc doc => doc -> doc -> doc
$$
SDoc
while_linking_expr
while_linking_expr :: SDoc
while_linking_expr = String -> SDoc
forall doc. IsLine doc => String -> doc
text String
"while linking an interpreted expression"
homeModLinkable :: HomeModInfo -> Maybe Linkable
homeModLinkable :: HomeModInfo -> Maybe Linkable
homeModLinkable HomeModInfo
hmi =
if LinkDepsOpts -> Bool
ldUseByteCode LinkDepsOpts
opts
then HomeModInfo -> Maybe Linkable
homeModInfoByteCode HomeModInfo
hmi Maybe Linkable -> Maybe Linkable -> Maybe Linkable
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> HomeModInfo -> Maybe Linkable
homeModInfoObject HomeModInfo
hmi
else HomeModInfo -> Maybe Linkable
homeModInfoObject HomeModInfo
hmi Maybe Linkable -> Maybe Linkable -> Maybe Linkable
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> HomeModInfo -> Maybe Linkable
homeModInfoByteCode HomeModInfo
hmi
get_linkable :: String -> Module -> IO Linkable
get_linkable String
osuf Module
mod
= Module -> HomeUnitGraph -> IO (Maybe HomeModInfo)
HUG.lookupHugByModule Module
mod (UnitEnv -> HomeUnitGraph
ue_home_unit_graph UnitEnv
unit_env) IO (Maybe HomeModInfo)
-> (Maybe HomeModInfo -> IO Linkable) -> IO Linkable
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Just HomeModInfo
mod_info -> Linkable -> IO Linkable
adjust_linkable (String -> Maybe Linkable -> Linkable
forall a. HasDebugCallStack => String -> Maybe a -> a
expectJust String
"getLinkDeps" (HomeModInfo -> Maybe Linkable
homeModLinkable HomeModInfo
mod_info))
Maybe HomeModInfo
Nothing -> do
case UnitEnv -> Maybe HomeUnit
ue_homeUnit UnitEnv
unit_env of
Maybe HomeUnit
Nothing -> Module -> IO Linkable
forall a b. Outputable a => a -> IO b
no_obj Module
mod
Just HomeUnit
home_unit -> do
from_bc <- LinkDepsOpts -> Module -> IO (Maybe Linkable)
ldLoadByteCode LinkDepsOpts
opts Module
mod
maybe (fallback_no_bytecode home_unit mod) pure from_bc
where
fallback_no_bytecode :: HomeUnit -> Module -> IO Linkable
fallback_no_bytecode HomeUnit
home_unit Module
mod = do
let fc :: FinderCache
fc = LinkDepsOpts -> FinderCache
ldFinderCache LinkDepsOpts
opts
let fopts :: FinderOpts
fopts = LinkDepsOpts -> FinderOpts
ldFinderOpts LinkDepsOpts
opts
mb_stuff <- FinderCache
-> FinderOpts -> HomeUnit -> ModuleName -> IO FindResult
findHomeModule FinderCache
fc FinderOpts
fopts HomeUnit
home_unit (Module -> ModuleName
forall unit. GenModule unit -> ModuleName
moduleName Module
mod)
case mb_stuff of
Found ModLocation
loc Module
_ -> do
mb_lnk <- Module -> ModLocation -> IO (Maybe Linkable)
findObjectLinkableMaybe Module
mod ModLocation
loc
case mb_lnk of
Maybe Linkable
Nothing -> Module -> IO Linkable
forall a b. Outputable a => a -> IO b
no_obj Module
mod
Just Linkable
lnk -> Linkable -> IO Linkable
adjust_linkable Linkable
lnk
FindResult
_ -> ModuleName -> IO Linkable
forall a b. Outputable a => a -> IO b
no_obj (Module -> ModuleName
forall unit. GenModule unit -> ModuleName
moduleName Module
mod)
adjust_linkable :: Linkable -> IO Linkable
adjust_linkable Linkable
lnk
| Just String
new_osuf <- Maybe String
maybe_normal_osuf = do
new_parts <- (LinkablePart -> IO LinkablePart)
-> NonEmpty LinkablePart -> IO (NonEmpty LinkablePart)
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> NonEmpty a -> m (NonEmpty b)
mapM (String -> LinkablePart -> IO LinkablePart
adjust_part String
new_osuf)
(Linkable -> NonEmpty LinkablePart
linkableParts Linkable
lnk)
return lnk{ linkableParts=new_parts }
| Bool
otherwise =
Linkable -> IO Linkable
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Linkable
lnk
adjust_part :: String -> LinkablePart -> IO LinkablePart
adjust_part String
new_osuf LinkablePart
part = case LinkablePart
part of
DotO String
file LinkableObjectSort
ModuleObject -> do
Bool -> IO ()
forall (m :: * -> *). (HasCallStack, Applicative m) => Bool -> m ()
massert (String
osuf String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
file)
let file_base :: String
file_base = Maybe String -> String
forall a. HasCallStack => Maybe a -> a
fromJust (String -> String -> Maybe String
stripExtension String
osuf String
file)
new_file :: String
new_file = String
file_base String -> String -> String
<.> String
new_osuf
ok <- String -> IO Bool
doesFileExist String
new_file
if (not ok)
then dieWith opts span $
text "cannot find object file "
<> quotes (text new_file) $$ while_linking_expr
else return (DotO new_file ModuleObject)
DotO String
file LinkableObjectSort
ForeignObject -> LinkablePart -> IO LinkablePart
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (String -> LinkableObjectSort -> LinkablePart
DotO String
file LinkableObjectSort
ForeignObject)
DotA String
fp -> String -> IO LinkablePart
forall a. HasCallStack => String -> a
panic (String
"adjust_ul DotA " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
forall a. Show a => a -> String
show String
fp)
DotDLL String
fp -> String -> IO LinkablePart
forall a. HasCallStack => String -> a
panic (String
"adjust_ul DotDLL " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
forall a. Show a => a -> String
show String
fp)
BCOs {} -> LinkablePart -> IO LinkablePart
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure LinkablePart
part
LazyBCOs{} -> LinkablePart -> IO LinkablePart
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure LinkablePart
part
CoreBindings WholeCoreBindings {Module
wcb_module :: Module
wcb_module :: WholeCoreBindings -> Module
wcb_module} ->
String -> SDoc -> IO LinkablePart
forall a. HasCallStack => String -> SDoc -> a
pprPanic String
"Unhydrated core bindings" (Module -> SDoc
forall a. Outputable a => a -> SDoc
ppr Module
wcb_module)
dieWith :: LinkDepsOpts -> SrcSpan -> SDoc -> IO a
dieWith :: forall a. LinkDepsOpts -> SrcSpan -> SDoc -> IO a
dieWith LinkDepsOpts
opts SrcSpan
span SDoc
msg = LinkDepsOpts -> SDoc -> IO a
forall a. LinkDepsOpts -> SDoc -> IO a
throwProgramError LinkDepsOpts
opts (MessageClass -> SrcSpan -> SDoc -> SDoc
mkLocMessage MessageClass
MCFatal SrcSpan
span SDoc
msg)
throwProgramError :: LinkDepsOpts -> SDoc -> IO a
throwProgramError :: forall a. LinkDepsOpts -> SDoc -> IO a
throwProgramError LinkDepsOpts
opts SDoc
doc = GhcException -> IO a
forall a. GhcException -> IO a
throwGhcExceptionIO (String -> GhcException
ProgramError (SDocContext -> SDoc -> String
renderWithContext (LinkDepsOpts -> SDocContext
ldPprOpts LinkDepsOpts
opts) SDoc
doc))
checkNonStdWay :: LinkDepsOpts -> Interp -> SrcSpan -> IO (Maybe FilePath)
checkNonStdWay :: LinkDepsOpts -> Interp -> SrcSpan -> IO (Maybe String)
checkNonStdWay LinkDepsOpts
_opts Interp
interp SrcSpan
_srcspan
| LinkDepsOpts -> Bool
ldForceDyn LinkDepsOpts
_opts = do
let target_ways :: Ways
target_ways = Ways -> Ways
fullWays (Ways -> Ways) -> Ways -> Ways
forall a b. (a -> b) -> a -> b
$ LinkDepsOpts -> Ways
ldWays LinkDepsOpts
_opts
Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe String -> IO (Maybe String))
-> Maybe String -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$ if Ways
target_ways Ways -> Way -> Bool
`hasWay` Way
WayDyn
then Maybe String
forall a. Maybe a
Nothing
else String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ Ways -> String
waysTag (Way
WayDyn Way -> Ways -> Ways
`addWay` Ways
target_ways) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"_o"
| ExternalInterp {} <- Interp -> InterpInstance
interpInstance Interp
interp = 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
#if defined(HAVE_INTERNAL_INTERPRETER)
checkNonStdWay LinkDepsOpts
opts Interp
_interp SrcSpan
srcspan
| Ways
hostFullWays Ways -> Ways -> Bool
forall a. Eq a => a -> a -> Bool
== Ways
targetFullWays = 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
| LinkDepsOpts -> String
ldObjSuffix LinkDepsOpts
opts String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
normalObjectSuffix Bool -> Bool -> Bool
&& Bool -> Bool
not (Ways -> Bool
forall a. Set a -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null Ways
targetFullWays)
= LinkDepsOpts -> SrcSpan -> IO (Maybe String)
failNonStd LinkDepsOpts
opts SrcSpan
srcspan
| Bool
otherwise = Maybe String -> IO (Maybe String)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Maybe String
forall a. a -> Maybe a
Just (String
hostWayTag String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"o"))
where
targetFullWays :: Ways
targetFullWays = Ways -> Ways
fullWays (LinkDepsOpts -> Ways
ldWays LinkDepsOpts
opts)
hostWayTag :: String
hostWayTag = case Ways -> String
waysTag Ways
hostFullWays of
String
"" -> String
""
String
tag -> String
tag String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"_"
normalObjectSuffix :: String
normalObjectSuffix :: String
normalObjectSuffix = String
"o"
data Way' = Normal | Prof | Dyn | ProfDyn
failNonStd :: LinkDepsOpts -> SrcSpan -> IO (Maybe FilePath)
failNonStd :: LinkDepsOpts -> SrcSpan -> IO (Maybe String)
failNonStd LinkDepsOpts
opts SrcSpan
srcspan = LinkDepsOpts -> SrcSpan -> SDoc -> IO (Maybe String)
forall a. LinkDepsOpts -> SrcSpan -> SDoc -> IO a
dieWith LinkDepsOpts
opts SrcSpan
srcspan (SDoc -> IO (Maybe String)) -> SDoc -> IO (Maybe String)
forall a b. (a -> b) -> a -> b
$
String -> SDoc
forall doc. IsLine doc => String -> doc
text String
"Cannot load" SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<+> Way' -> SDoc
pprWay' Way'
compWay SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<+>
String -> SDoc
forall doc. IsLine doc => String -> doc
text String
"objects when GHC is built" SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<+> Way' -> SDoc
pprWay' Way'
ghciWay SDoc -> SDoc -> SDoc
forall doc. IsDoc doc => doc -> doc -> doc
$$
String -> SDoc
forall doc. IsLine doc => String -> doc
text String
"To fix this, either:" SDoc -> SDoc -> SDoc
forall doc. IsDoc doc => doc -> doc -> doc
$$
String -> SDoc
forall doc. IsLine doc => String -> doc
text String
" (1) Use -fexternal-interpreter, or" SDoc -> SDoc -> SDoc
forall doc. IsDoc doc => doc -> doc -> doc
$$
SDoc
buildTwiceMsg
where compWay :: Way'
compWay
| LinkDepsOpts -> Ways
ldWays LinkDepsOpts
opts Ways -> Way -> Bool
`hasWay` Way
WayDyn Bool -> Bool -> Bool
&& LinkDepsOpts -> Ways
ldWays LinkDepsOpts
opts Ways -> Way -> Bool
`hasWay` Way
WayProf = Way'
ProfDyn
| LinkDepsOpts -> Ways
ldWays LinkDepsOpts
opts Ways -> Way -> Bool
`hasWay` Way
WayDyn = Way'
Dyn
| LinkDepsOpts -> Ways
ldWays LinkDepsOpts
opts Ways -> Way -> Bool
`hasWay` Way
WayProf = Way'
Prof
| Bool
otherwise = Way'
Normal
ghciWay :: Way'
ghciWay
| Bool
hostIsDynamic Bool -> Bool -> Bool
&& Bool
hostIsProfiled = Way'
ProfDyn
| Bool
hostIsDynamic = Way'
Dyn
| Bool
hostIsProfiled = Way'
Prof
| Bool
otherwise = Way'
Normal
buildTwiceMsg :: SDoc
buildTwiceMsg = case (Way'
ghciWay, Way'
compWay) of
(Way'
Normal, Way'
Dyn) -> SDoc
dynamicTooMsg
(Way'
Dyn, Way'
Normal) -> SDoc
dynamicTooMsg
(Way', Way')
_ ->
String -> SDoc
forall doc. IsLine doc => String -> doc
text String
" (2) Build the program twice: once" SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<+>
Way' -> SDoc
pprWay' Way'
ghciWay SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<> String -> SDoc
forall doc. IsLine doc => String -> doc
text String
", and then" SDoc -> SDoc -> SDoc
forall doc. IsDoc doc => doc -> doc -> doc
$$
String -> SDoc
forall doc. IsLine doc => String -> doc
text String
" " SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<> Way' -> SDoc
pprWay' Way'
compWay SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<+>
String -> SDoc
forall doc. IsLine doc => String -> doc
text String
"using -osuf to set a different object file suffix."
dynamicTooMsg :: SDoc
dynamicTooMsg = String -> SDoc
forall doc. IsLine doc => String -> doc
text String
" (2) Use -dynamic-too," SDoc -> SDoc -> SDoc
forall doc. IsLine doc => doc -> doc -> doc
<+>
String -> SDoc
forall doc. IsLine doc => String -> doc
text String
"and use -osuf and -dynosuf to set object file suffixes as needed."
pprWay' :: Way' -> SDoc
pprWay' :: Way' -> SDoc
pprWay' Way'
way = String -> SDoc
forall doc. IsLine doc => String -> doc
text (String -> SDoc) -> String -> SDoc
forall a b. (a -> b) -> a -> b
$ case Way'
way of
Way'
Normal -> String
"the normal way"
Way'
Prof -> String
"with -prof"
Way'
Dyn -> String
"with -dynamic"
Way'
ProfDyn -> String
"with -prof and -dynamic"
#endif