%
% (c) The University of Glasgow 2005-2006
%
\begin{code}
module Linker ( HValue, getHValue, showLinkerState,
linkExpr, unload, withExtendedLinkEnv,
extendLinkEnv, deleteFromLinkEnv,
extendLoadedPkgs,
linkPackages,initDynLinker,
dataConInfoPtrToName
) where
#include "HsVersions.h"
import LoadIface
import ObjLink
import ByteCodeLink
import ByteCodeItbls
import ByteCodeAsm
import CgInfoTbls
import SMRep
import IfaceEnv
import TcRnMonad
import Packages
import DriverPhases
import Finder
import HscTypes
import Name
import NameEnv
import NameSet
import qualified OccName
import UniqFM
import Module
import ListSetOps
import DynFlags
import BasicTypes
import Outputable
import Panic
import Util
import StaticFlags
import ErrUtils
import SrcLoc
import qualified Maybes
import UniqSet
import Constants
import FastString
import Config
import Control.Monad
import Data.Char
import Data.IORef
import Data.List
import qualified Data.Map as Map
import Foreign
import Control.Concurrent.MVar
import System.FilePath
import System.IO
import System.Directory
import Distribution.Package hiding (depends, PackageId)
import Exception
\end{code}
%************************************************************************
%* *
The Linker's state
%* *
%************************************************************************
The persistent linker state *must* match the actual state of the
C dynamic linker at all times, so we keep it in a private global variable.
The PersistentLinkerState maps Names to actual closures (for
interpreted code only), for use during linking.
\begin{code}
GLOBAL_MVAR(v_PersistentLinkerState, panic "Dynamic linker not initialised", PersistentLinkerState)
GLOBAL_VAR(v_InitLinkerDone, False, Bool)
data PersistentLinkerState
= PersistentLinkerState {
closure_env :: ClosureEnv,
itbl_env :: !ItblEnv,
bcos_loaded :: ![Linkable],
objs_loaded :: ![Linkable],
pkgs_loaded :: ![PackageId]
}
emptyPLS :: DynFlags -> PersistentLinkerState
emptyPLS _ = PersistentLinkerState {
closure_env = emptyNameEnv,
itbl_env = emptyNameEnv,
pkgs_loaded = init_pkgs,
bcos_loaded = [],
objs_loaded = [] }
where init_pkgs = [rtsPackageId]
\end{code}
\begin{code}
extendLoadedPkgs :: [PackageId] -> IO ()
extendLoadedPkgs pkgs =
modifyMVar_ v_PersistentLinkerState $ \s ->
return s{ pkgs_loaded = pkgs ++ pkgs_loaded s }
extendLinkEnv :: [(Name,HValue)] -> IO ()
extendLinkEnv new_bindings =
modifyMVar_ v_PersistentLinkerState $ \pls ->
let new_closure_env = extendClosureEnv (closure_env pls) new_bindings
in return pls{ closure_env = new_closure_env }
deleteFromLinkEnv :: [Name] -> IO ()
deleteFromLinkEnv to_remove =
modifyMVar_ v_PersistentLinkerState $ \pls ->
let new_closure_env = delListFromNameEnv (closure_env pls) to_remove
in return pls{ closure_env = new_closure_env }
dataConInfoPtrToName :: Ptr () -> TcM (Either String Name)
dataConInfoPtrToName x = do
theString <- liftIO $ do
let ptr = castPtr x :: Ptr StgInfoTable
conDescAddress <- getConDescAddress ptr
peekArray0 0 conDescAddress
let (pkg, mod, occ) = parse theString
pkgFS = mkFastStringByteList pkg
modFS = mkFastStringByteList mod
occFS = mkFastStringByteList occ
occName = mkOccNameFS OccName.dataName occFS
modName = mkModule (fsToPackageId pkgFS) (mkModuleNameFS modFS)
return (Left$ showSDoc$ ppr modName <> dot <> ppr occName )
`recoverM` (Right `fmap` lookupOrig modName occName)
where
getConDescAddress :: Ptr StgInfoTable -> IO (Ptr Word8)
getConDescAddress ptr
| ghciTablesNextToCode = do
offsetToString <- peek $ ptr `plusPtr` ( wORD_SIZE)
return $ (ptr `plusPtr` stdInfoTableSizeB) `plusPtr` (fromIntegral (offsetToString :: StgWord))
| otherwise =
peek $ intPtrToPtr $ (ptrToIntPtr ptr) + fromIntegral stdInfoTableSizeB
parse :: [Word8] -> ([Word8], [Word8], [Word8])
parse input
= ASSERT (all (>0) (map length [pkg, mod, occ])) (pkg, mod, occ)
where
dot = fromIntegral (ord '.')
(pkg, rest1) = break (== fromIntegral (ord ':')) input
(mod, occ)
= (concat $ intersperse [dot] $ reverse modWords, occWord)
where
(modWords, occWord) = ASSERT (length rest1 > 0) (parseModOcc [] (tail rest1))
parseModOcc :: [[Word8]] -> [Word8] -> ([[Word8]], [Word8])
parseModOcc acc str
= case break (== dot) str of
(top, []) -> (acc, top)
(top, _:bot) -> parseModOcc (top : acc) bot
getHValue :: HscEnv -> Name -> IO HValue
getHValue hsc_env name = do
pls <- modifyMVar v_PersistentLinkerState $ \pls -> do
if (isExternalName name) then do
(pls', ok) <- linkDependencies hsc_env pls noSrcSpan [nameModule name]
if (failed ok) then ghcError (ProgramError "")
else return (pls', pls')
else
return (pls, pls)
lookupName (closure_env pls) name
linkDependencies :: HscEnv -> PersistentLinkerState
-> SrcSpan -> [Module]
-> IO (PersistentLinkerState, SuccessFlag)
linkDependencies hsc_env pls span needed_mods = do
let hpt = hsc_HPT hsc_env
dflags = hsc_dflags hsc_env
maybe_normal_osuf <- checkNonStdWay dflags span
(lnks, pkgs) <- getLinkDeps hsc_env hpt pls
maybe_normal_osuf span needed_mods
pls1 <- linkPackages' dflags pkgs pls
linkModules dflags pls1 lnks
withExtendedLinkEnv :: (MonadIO m, ExceptionMonad m) =>
[(Name,HValue)] -> m a -> m a
withExtendedLinkEnv new_env action
= gbracket (liftIO $ extendLinkEnv new_env)
(\_ -> reset_old_env)
(\_ -> action)
where
reset_old_env = liftIO $ do
modifyMVar_ v_PersistentLinkerState $ \pls ->
let cur = closure_env pls
new = delListFromNameEnv cur (map fst new_env)
in return pls{ closure_env = new }
filterNameMap :: [Module] -> NameEnv (Name, a) -> NameEnv (Name, a)
filterNameMap mods env
= filterNameEnv keep_elt env
where
keep_elt (n,_) = isExternalName n
&& (nameModule n `elem` mods)
\end{code}
\begin{code}
showLinkerState :: IO ()
showLinkerState
= do pls <- readMVar v_PersistentLinkerState
printDump (vcat [text "----- Linker state -----",
text "Pkgs:" <+> ppr (pkgs_loaded pls),
text "Objs:" <+> ppr (objs_loaded pls),
text "BCOs:" <+> ppr (bcos_loaded pls)])
\end{code}
%************************************************************************
%* *
\subsection{Initialisation}
%* *
%************************************************************************
\begin{code}
initDynLinker :: DynFlags -> IO ()
initDynLinker dflags =
modifyMVar_ v_PersistentLinkerState $ \pls0 -> do
done <- readIORef v_InitLinkerDone
if done then return pls0
else do writeIORef v_InitLinkerDone True
reallyInitDynLinker dflags
reallyInitDynLinker :: DynFlags -> IO PersistentLinkerState
reallyInitDynLinker dflags =
do {
let pls0 = emptyPLS dflags
; initObjLinker
; pls <- linkPackages' dflags (preloadPackages (pkgState dflags)) pls0
; let optl = getOpts dflags opt_l
; let minus_ls = [ lib | '-':'l':lib <- optl ]
; let lib_paths = libraryPaths dflags
; cmdline_ld_inputs <- readIORef v_Ld_inputs
; classified_ld_inputs <- mapM classifyLdInput cmdline_ld_inputs
; let framework_paths
| isDarwinTarget = frameworkPaths dflags
| otherwise = []
; let frameworks
| isDarwinTarget = cmdlineFrameworks dflags
| otherwise = []
; let cmdline_lib_specs = [ l | Just l <- classified_ld_inputs ]
++ map DLL minus_ls
++ map Framework frameworks
; if null cmdline_lib_specs then return pls
else do
{ mapM_ (preloadLib dflags lib_paths framework_paths) cmdline_lib_specs
; maybePutStr dflags "final link ... "
; ok <- resolveObjs
; if succeeded ok then maybePutStrLn dflags "done"
else ghcError (ProgramError "linking extra libraries/objects failed")
; return pls
}}
classifyLdInput :: FilePath -> IO (Maybe LibrarySpec)
classifyLdInput f
| isObjectFilename f = return (Just (Object f))
| isDynLibFilename f = return (Just (DLLPath f))
| otherwise = do
hPutStrLn stderr ("Warning: ignoring unrecognised input `" ++ f ++ "'")
return Nothing
preloadLib :: DynFlags -> [String] -> [String] -> LibrarySpec -> IO ()
preloadLib dflags lib_paths framework_paths lib_spec
= do maybePutStr dflags ("Loading object " ++ showLS lib_spec ++ " ... ")
case lib_spec of
Object static_ish
-> do b <- preload_static lib_paths static_ish
maybePutStrLn dflags (if b then "done"
else "not found")
Archive static_ish
-> do b <- preload_static_archive lib_paths static_ish
maybePutStrLn dflags (if b then "done"
else "not found")
DLL dll_unadorned
-> do maybe_errstr <- loadDynamic lib_paths dll_unadorned
case maybe_errstr of
Nothing -> maybePutStrLn dflags "done"
Just mm -> preloadFailed mm lib_paths lib_spec
DLLPath dll_path
-> do maybe_errstr <- loadDLL dll_path
case maybe_errstr of
Nothing -> maybePutStrLn dflags "done"
Just mm -> preloadFailed mm lib_paths lib_spec
Framework framework
| isDarwinTarget
-> do maybe_errstr <- loadFramework framework_paths framework
case maybe_errstr of
Nothing -> maybePutStrLn dflags "done"
Just mm -> preloadFailed mm framework_paths lib_spec
| otherwise -> panic "preloadLib Framework"
where
preloadFailed :: String -> [String] -> LibrarySpec -> IO ()
preloadFailed sys_errmsg paths spec
= do maybePutStr dflags "failed.\n"
ghcError $
CmdLineError (
"user specified .o/.so/.DLL could not be loaded ("
++ sys_errmsg ++ ")\nWhilst trying to load: "
++ showLS spec ++ "\nAdditional directories searched:"
++ (if null paths then " (none)" else
(concat (intersperse "\n" (map (" "++) paths)))))
preload_static _paths name
= do b <- doesFileExist name
if not b then return False
else loadObj name >> return True
preload_static_archive _paths name
= do b <- doesFileExist name
if not b then return False
else loadArchive name >> return True
\end{code}
%************************************************************************
%* *
Link a byte-code expression
%* *
%************************************************************************
\begin{code}
linkExpr :: HscEnv -> SrcSpan -> UnlinkedBCO -> IO HValue
linkExpr hsc_env span root_ul_bco
= do {
let dflags = hsc_dflags hsc_env
; initDynLinker dflags
; modifyMVar v_PersistentLinkerState $ \pls0 -> do {
; (pls, ok) <- linkDependencies hsc_env pls0 span needed_mods
; if failed ok then
ghcError (ProgramError "")
else do {
let ie = itbl_env pls
ce = closure_env pls
; (_, (root_hval:_)) <- linkSomeBCOs False ie ce [root_ul_bco]
; return (pls, root_hval)
}}}
where
free_names = nameSetToList (bcoFreeNames root_ul_bco)
needed_mods :: [Module]
needed_mods = [ nameModule n | n <- free_names,
isExternalName n,
not (isWiredInName n)
]
dieWith :: SrcSpan -> Message -> IO a
dieWith span msg = ghcError (ProgramError (showSDoc (mkLocMessage span msg)))
checkNonStdWay :: DynFlags -> SrcSpan -> IO (Maybe String)
checkNonStdWay dflags srcspan = do
let tag = buildTag dflags
if null tag then return Nothing else do
let default_osuf = phaseInputExt StopLn
if objectSuf dflags == default_osuf
then failNonStd srcspan
else return (Just default_osuf)
failNonStd :: SrcSpan -> IO (Maybe String)
failNonStd srcspan = dieWith srcspan $
ptext (sLit "Dynamic linking required, but this is a non-standard build (eg. prof).") $$
ptext (sLit "You need to build the program twice: once the normal way, and then") $$
ptext (sLit "in the desired way using -osuf to set the object file suffix.")
getLinkDeps :: HscEnv -> HomePackageTable
-> PersistentLinkerState
-> Maybe String
-> SrcSpan
-> [Module]
-> IO ([Linkable], [PackageId])
getLinkDeps hsc_env hpt pls maybe_normal_osuf span mods
= do {
(mods_s, pkgs_s) <- follow_deps mods emptyUniqSet emptyUniqSet;
let {
mods_needed = mods_s `minusList` linked_mods ;
pkgs_needed = pkgs_s `minusList` pkgs_loaded pls ;
linked_mods = map (moduleName.linkableModule)
(objs_loaded pls ++ bcos_loaded pls)
} ;
lnks_needed <- mapM (get_linkable maybe_normal_osuf) mods_needed ;
return (lnks_needed, pkgs_needed) }
where
dflags = hsc_dflags hsc_env
this_pkg = thisPackage dflags
follow_deps :: [Module]
-> UniqSet ModuleName
-> UniqSet PackageId
-> IO ([ModuleName], [PackageId])
follow_deps [] acc_mods acc_pkgs
= return (uniqSetToList acc_mods, uniqSetToList acc_pkgs)
follow_deps (mod:mods) acc_mods acc_pkgs
= do
mb_iface <- initIfaceCheck hsc_env $
loadInterface msg mod (ImportByUser False)
iface <- case mb_iface of
Maybes.Failed err -> ghcError (ProgramError (showSDoc err))
Maybes.Succeeded iface -> return iface
when (mi_boot iface) $ link_boot_mod_error mod
let
pkg = modulePackageId mod
deps = mi_deps iface
pkg_deps = dep_pkgs deps
(boot_deps, mod_deps) = partitionWith is_boot (dep_mods deps)
where is_boot (m,True) = Left m
is_boot (m,False) = Right m
boot_deps' = filter (not . (`elementOfUniqSet` acc_mods)) boot_deps
acc_mods' = addListToUniqSet acc_mods (moduleName mod : mod_deps)
acc_pkgs' = addListToUniqSet acc_pkgs pkg_deps
if pkg /= this_pkg
then follow_deps mods acc_mods (addOneToUniqSet acc_pkgs' pkg)
else follow_deps (map (mkModule this_pkg) boot_deps' ++ mods)
acc_mods' acc_pkgs'
where
msg = text "need to link module" <+> ppr mod <+>
text "due to use of Template Haskell"
link_boot_mod_error mod =
ghcError (ProgramError (showSDoc (
text "module" <+> ppr mod <+>
text "cannot be linked; it is only available as a boot module")))
no_obj :: Outputable a => a -> IO b
no_obj mod = dieWith span $
ptext (sLit "cannot find object file for module ") <>
quotes (ppr mod) $$
while_linking_expr
while_linking_expr = ptext (sLit "while linking an interpreted expression")
get_linkable maybe_normal_osuf mod_name
| Just mod_info <- lookupUFM hpt mod_name
= adjust_linkable (Maybes.expectJust "getLinkDeps" (hm_linkable mod_info))
| otherwise
= do
mb_stuff <- findHomeModule hsc_env mod_name
case mb_stuff of
Found loc mod -> found loc mod
_ -> no_obj mod_name
where
found loc mod = do {
mb_lnk <- findObjectLinkableMaybe mod loc ;
case mb_lnk of {
Nothing -> no_obj mod ;
Just lnk -> adjust_linkable lnk
}}
adjust_linkable lnk
| Just osuf <- maybe_normal_osuf = do
new_uls <- mapM (adjust_ul osuf) (linkableUnlinked lnk)
return lnk{ linkableUnlinked=new_uls }
| otherwise =
return lnk
adjust_ul osuf (DotO file) = do
let new_file = replaceExtension file osuf
ok <- doesFileExist new_file
if (not ok)
then dieWith span $
ptext (sLit "cannot find normal object file ")
<> quotes (text new_file) $$ while_linking_expr
else return (DotO new_file)
adjust_ul _ _ = panic "adjust_ul"
\end{code}
%************************************************************************
%* *
Link some linkables
The linkables may consist of a mixture of
byte-code modules and object modules
%* *
%************************************************************************
\begin{code}
linkModules :: DynFlags -> PersistentLinkerState -> [Linkable]
-> IO (PersistentLinkerState, SuccessFlag)
linkModules dflags pls linkables
= mask_ $ do
let (objs, bcos) = partition isObjectLinkable
(concatMap partitionLinkable linkables)
(pls1, ok_flag) <- dynLinkObjs dflags pls objs
if failed ok_flag then
return (pls1, Failed)
else do
pls2 <- dynLinkBCOs pls1 bcos
return (pls2, Succeeded)
partitionLinkable :: Linkable -> [Linkable]
partitionLinkable li
= let li_uls = linkableUnlinked li
li_uls_obj = filter isObject li_uls
li_uls_bco = filter isInterpretable li_uls
in
case (li_uls_obj, li_uls_bco) of
(_:_, _:_) -> [li {linkableUnlinked=li_uls_obj},
li {linkableUnlinked=li_uls_bco}]
_ -> [li]
findModuleLinkable_maybe :: [Linkable] -> Module -> Maybe Linkable
findModuleLinkable_maybe lis mod
= case [LM time nm us | LM time nm us <- lis, nm == mod] of
[] -> Nothing
[li] -> Just li
_ -> pprPanic "findModuleLinkable" (ppr mod)
linkableInSet :: Linkable -> [Linkable] -> Bool
linkableInSet l objs_loaded =
case findModuleLinkable_maybe objs_loaded (linkableModule l) of
Nothing -> False
Just m -> linkableTime l == linkableTime m
\end{code}
%************************************************************************
%* *
\subsection{The object-code linker}
%* *
%************************************************************************
\begin{code}
dynLinkObjs :: DynFlags -> PersistentLinkerState -> [Linkable]
-> IO (PersistentLinkerState, SuccessFlag)
dynLinkObjs dflags pls objs = do
let (objs_loaded', new_objs) = rmDupLinkables (objs_loaded pls) objs
pls1 = pls { objs_loaded = objs_loaded' }
unlinkeds = concatMap linkableUnlinked new_objs
mapM_ loadObj (map nameOfObject unlinkeds)
ok <- resolveObjs
if succeeded ok then do
return (pls1, Succeeded)
else do
pls2 <- unload_wkr dflags [] pls1
return (pls2, Failed)
rmDupLinkables :: [Linkable]
-> [Linkable]
-> ([Linkable],
[Linkable])
rmDupLinkables already ls
= go already [] ls
where
go already extras [] = (already, extras)
go already extras (l:ls)
| linkableInSet l already = go already extras ls
| otherwise = go (l:already) (l:extras) ls
\end{code}
%************************************************************************
%* *
\subsection{The byte-code linker}
%* *
%************************************************************************
\begin{code}
dynLinkBCOs :: PersistentLinkerState -> [Linkable] -> IO PersistentLinkerState
dynLinkBCOs pls bcos = do
let (bcos_loaded', new_bcos) = rmDupLinkables (bcos_loaded pls) bcos
pls1 = pls { bcos_loaded = bcos_loaded' }
unlinkeds :: [Unlinked]
unlinkeds = concatMap linkableUnlinked new_bcos
cbcs :: [CompiledByteCode]
cbcs = map byteCodeOfObject unlinkeds
ul_bcos = [b | ByteCode bs _ <- cbcs, b <- bs]
ies = [ie | ByteCode _ ie <- cbcs]
gce = closure_env pls
final_ie = foldr plusNameEnv (itbl_env pls) ies
(final_gce, _linked_bcos) <- linkSomeBCOs True final_ie gce ul_bcos
let pls2 = pls1 { closure_env = final_gce,
itbl_env = final_ie }
return pls2
linkSomeBCOs :: Bool
-> ItblEnv
-> ClosureEnv
-> [UnlinkedBCO]
-> IO (ClosureEnv, [HValue])
linkSomeBCOs toplevs_only ie ce_in ul_bcos
= do let nms = map unlinkedBCOName ul_bcos
hvals <- fixIO
( \ hvs -> let ce_out = extendClosureEnv ce_in (zipLazy nms hvs)
in mapM (linkBCO ie ce_out) ul_bcos )
let ce_all_additions = zip nms hvals
ce_top_additions = filter (isExternalName.fst) ce_all_additions
ce_additions = if toplevs_only then ce_top_additions
else ce_all_additions
ce_out =
ASSERT (all (not . (`elemNameEnv` ce_in)) (map fst ce_additions))
extendClosureEnv ce_in ce_additions
return (ce_out, hvals)
\end{code}
%************************************************************************
%* *
Unload some object modules
%* *
%************************************************************************
\begin{code}
unload :: DynFlags
-> [Linkable]
-> IO ()
unload dflags linkables
= mask_ $ do
initDynLinker dflags
new_pls
<- modifyMVar v_PersistentLinkerState $ \pls -> do
pls1 <- unload_wkr dflags linkables pls
return (pls1, pls1)
debugTraceMsg dflags 3 (text "unload: retaining objs" <+> ppr (objs_loaded new_pls))
debugTraceMsg dflags 3 (text "unload: retaining bcos" <+> ppr (bcos_loaded new_pls))
return ()
unload_wkr :: DynFlags
-> [Linkable]
-> PersistentLinkerState
-> IO PersistentLinkerState
unload_wkr _ linkables pls
= do let (objs_to_keep, bcos_to_keep) = partition isObjectLinkable linkables
objs_loaded' <- filterM (maybeUnload objs_to_keep) (objs_loaded pls)
bcos_loaded' <- filterM (maybeUnload bcos_to_keep) (bcos_loaded pls)
let bcos_retained = map linkableModule bcos_loaded'
itbl_env' = filterNameMap bcos_retained (itbl_env pls)
closure_env' = filterNameMap bcos_retained (closure_env pls)
new_pls = pls { itbl_env = itbl_env',
closure_env = closure_env',
bcos_loaded = bcos_loaded',
objs_loaded = objs_loaded' }
return new_pls
where
maybeUnload :: [Linkable] -> Linkable -> IO Bool
maybeUnload keep_linkables lnk
| linkableInSet lnk keep_linkables = return True
| otherwise
= do mapM_ unloadObj [f | DotO f <- linkableUnlinked lnk]
return False
\end{code}
%************************************************************************
%* *
Loading packages
%* *
%************************************************************************
\begin{code}
data LibrarySpec
= Object FilePath
| Archive FilePath
| DLL String
| DLLPath FilePath
| Framework String
partOfGHCi :: [PackageName]
partOfGHCi
| isWindowsTarget || isDarwinTarget = []
| otherwise = map PackageName
["base", "template-haskell", "editline"]
showLS :: LibrarySpec -> String
showLS (Object nm) = "(static) " ++ nm
showLS (Archive nm) = "(static archive) " ++ nm
showLS (DLL nm) = "(dynamic) " ++ nm
showLS (DLLPath nm) = "(dynamic) " ++ nm
showLS (Framework nm) = "(framework) " ++ nm
linkPackages :: DynFlags -> [PackageId] -> IO ()
linkPackages dflags new_pkgs = do
modifyMVar_ v_PersistentLinkerState $ \pls -> do
linkPackages' dflags new_pkgs pls
linkPackages' :: DynFlags -> [PackageId] -> PersistentLinkerState
-> IO PersistentLinkerState
linkPackages' dflags new_pks pls = do
pkgs' <- link (pkgs_loaded pls) new_pks
return $! pls { pkgs_loaded = pkgs' }
where
pkg_map = pkgIdMap (pkgState dflags)
ipid_map = installedPackageIdMap (pkgState dflags)
link :: [PackageId] -> [PackageId] -> IO [PackageId]
link pkgs new_pkgs =
foldM link_one pkgs new_pkgs
link_one pkgs new_pkg
| new_pkg `elem` pkgs
= return pkgs
| Just pkg_cfg <- lookupPackage pkg_map new_pkg
= do {
pkgs' <- link pkgs [ Maybes.expectJust "link_one" $
Map.lookup ipid ipid_map
| ipid <- depends pkg_cfg ]
; linkPackage dflags pkg_cfg
; return (new_pkg : pkgs') }
| otherwise
= ghcError (CmdLineError ("unknown package: " ++ packageIdString new_pkg))
linkPackage :: DynFlags -> PackageConfig -> IO ()
linkPackage dflags pkg
= do
let dirs = Packages.libraryDirs pkg
let libs = Packages.hsLibraries pkg
libs' = filter ("HSffi" /=) libs
++ (if null (Packages.extraGHCiLibraries pkg)
then Packages.extraLibraries pkg
else Packages.extraGHCiLibraries pkg)
++ [ lib | '-':'l':lib <- Packages.ldOptions pkg ]
classifieds <- mapM (locateOneObj dirs) libs'
let dlls = [ dll | DLL dll <- classifieds ]
objs = [ obj | Object obj <- classifieds ]
archs = [ arch | Archive arch <- classifieds ]
maybePutStr dflags ("Loading package " ++ display (sourcePackageId pkg) ++ " ... ")
when (packageName pkg `notElem` partOfGHCi) $ do
loadFrameworks pkg
mapM_ (load_dyn dirs) (reverse dlls)
mapM_ loadObj objs
mapM_ loadArchive archs
maybePutStr dflags "linking ... "
ok <- resolveObjs
if succeeded ok then maybePutStrLn dflags "done."
else ghcError (InstallationError ("unable to load package `" ++ display (sourcePackageId pkg) ++ "'"))
load_dyn :: [FilePath] -> FilePath -> IO ()
load_dyn dirs dll = do r <- loadDynamic dirs dll
case r of
Nothing -> return ()
Just err -> ghcError (CmdLineError ("can't load .so/.DLL for: "
++ dll ++ " (" ++ err ++ ")" ))
loadFrameworks :: InstalledPackageInfo_ ModuleName -> IO ()
loadFrameworks pkg
| isDarwinTarget = mapM_ load frameworks
| otherwise = return ()
where
fw_dirs = Packages.frameworkDirs pkg
frameworks = Packages.frameworks pkg
load fw = do r <- loadFramework fw_dirs fw
case r of
Nothing -> return ()
Just err -> ghcError (CmdLineError ("can't load framework: "
++ fw ++ " (" ++ err ++ ")" ))
locateOneObj :: [FilePath] -> String -> IO LibrarySpec
locateOneObj dirs lib
| not ("HS" `isPrefixOf` lib)
= assumeDll
| not isDynamicGhcLib
= findObject `orElse` findArchive `orElse` assumeDll
| otherwise
= findDll `orElse` findObject `orElse` findArchive `orElse` assumeDll
where
mk_obj_path dir = dir </> (lib <.> "o")
mk_arch_path dir = dir </> ("lib" ++ lib <.> "a")
dyn_lib_name = lib ++ "-ghc" ++ cProjectVersion
mk_dyn_lib_path dir = dir </> mkSOName dyn_lib_name
findObject = liftM (fmap Object) $ findFile mk_obj_path dirs
findArchive = liftM (fmap Archive) $ findFile mk_arch_path dirs
findDll = liftM (fmap DLL) $ findFile mk_dyn_lib_path dirs
assumeDll = return (DLL lib)
infixr `orElse`
f `orElse` g = do m <- f
case m of
Just x -> return x
Nothing -> g
loadDynamic :: [FilePath] -> FilePath -> IO (Maybe String)
loadDynamic paths rootname
= do { mb_dll <- findFile mk_dll_path paths
; case mb_dll of
Just dll -> loadDLL dll
Nothing -> loadDLL (mkSOName rootname) }
where
mk_dll_path dir = dir </> mkSOName rootname
mkSOName :: FilePath -> FilePath
mkSOName root
| isDarwinTarget = ("lib" ++ root) <.> "dylib"
| isWindowsTarget =
root
| otherwise = ("lib" ++ root) <.> "so"
loadFramework :: [FilePath] -> FilePath -> IO (Maybe String)
loadFramework extraPaths rootname
= do { either_dir <- tryIO getHomeDirectory
; let homeFrameworkPath = case either_dir of
Left _ -> []
Right dir -> [dir ++ "/Library/Frameworks"]
ps = extraPaths ++ homeFrameworkPath ++ defaultFrameworkPaths
; mb_fwk <- findFile mk_fwk ps
; case mb_fwk of
Just fwk_path -> loadDLL fwk_path
Nothing -> return (Just "not found") }
where
mk_fwk dir = dir </> (rootname ++ ".framework/" ++ rootname)
defaultFrameworkPaths = ["/Library/Frameworks", "/System/Library/Frameworks"]
\end{code}
%************************************************************************
%* *
Helper functions
%* *
%************************************************************************
\begin{code}
findFile :: (FilePath -> FilePath)
-> [FilePath]
-> IO (Maybe FilePath)
findFile _ []
= return Nothing
findFile mk_file_path (dir:dirs)
= do { let file_path = mk_file_path dir
; b <- doesFileExist file_path
; if b then
return (Just file_path)
else
findFile mk_file_path dirs }
\end{code}
\begin{code}
maybePutStr :: DynFlags -> String -> IO ()
maybePutStr dflags s | verbosity dflags > 0 = putStr s
| otherwise = return ()
maybePutStrLn :: DynFlags -> String -> IO ()
maybePutStrLn dflags s | verbosity dflags > 0 = putStrLn s
| otherwise = return ()
\end{code}