{-# OPTIONS -#include "Linker.h" -#include "SchedAPI.h" #-}
-----------------------------------------------------------------------------
--- $Id: InteractiveUI.hs,v 1.112 2002/01/28 13:34:10 simonmar Exp $
+-- $Id: InteractiveUI.hs,v 1.129 2002/07/17 13:49:15 simonmar Exp $
--
-- GHC Interactive User Interface
--
-- (c) The GHC Team 2000
--
-----------------------------------------------------------------------------
-module InteractiveUI ( interactiveUI, ghciWelcomeMsg ) where
+module InteractiveUI (
+ interactiveUI, -- :: CmState -> [FilePath] -> [LibrarySpec] -> IO ()
+ LibrarySpec(..),
+ ghciWelcomeMsg
+ ) where
#include "../includes/config.h"
#include "HsVersions.h"
import Id ( isRecordSelector, recordSelectorFieldLabel,
isDataConWrapId, isDataConId, idName )
import Class ( className )
-import TyCon ( tyConName, tyConClass_maybe, isPrimTyCon )
+import TyCon ( tyConName, tyConClass_maybe, isPrimTyCon, DataConDetails(..) )
import FieldLabel ( fieldLabelTyCon )
import SrcLoc ( isGoodSrcLoc )
import Module ( moduleName )
import Char
import Monad
-import PrelGHC ( unsafeCoerce# )
+import GlaExts ( unsafeCoerce# )
+
import Foreign ( nullPtr )
-import CString ( peekCString )
+import CString ( CString, peekCString, withCString )
-----------------------------------------------------------------------------
shortHelpText = "use :? for help.\n"
+-- NOTE: spaces at the end of each line to workaround CPP/string gap bug.
helpText = "\
\ Commands available from the prompt:\n\
-\\
-\ <stmt> evaluate/run <stmt>\n\
-\ :add <filename> ... add module(s) to the current target set\n\
-\ :browse [*]<module> display the names defined by <module>\n\
-\ :cd <dir> change directory to <dir>\n\
-\ :def <cmd> <expr> define a command :<cmd>\n\
-\ :help, :? display this list of commands\n\
-\ :info [<name> ...] display information about the given names\n\
-\ :load <filename> ... load module(s) and their dependents\n\
-\ :module <mod> set the context for expression evaluation to <mod>\n\
-\ :reload reload the current module set\n\
\\n\
-\ :set <option> ... set options\n\
-\ :set args <arg> ... set the arguments returned by System.getArgs\n\
-\ :set prog <progname> set the value returned by System.getProgName\n\
+\ <stmt> evaluate/run <stmt>\n\
+\ :add <filename> ... add module(s) to the current target set\n\
+\ :browse [*]<module> display the names defined by <module>\n\
+\ :cd <dir> change directory to <dir>\n\
+\ :def <cmd> <expr> define a command :<cmd>\n\
+\ :help, :? display this list of commands\n\
+\ :info [<name> ...] display information about the given names\n\
+\ :load <filename> ... load module(s) and their dependents\n\
+\ :module [+/-] [*]<mod> ... set the context for expression evaluation\n\
+\ :reload reload the current module set\n\
+\\n\
+\ :set <option> ... set options\n\
+\ :set args <arg> ... set the arguments returned by System.getArgs\n\
+\ :set prog <progname> set the value returned by System.getProgName\n\
\\n\
-\ :show modules show the currently loaded modules\n\
-\ :show bindings show the current bindings made at the prompt\n\
+\ :show modules show the currently loaded modules\n\
+\ :show bindings show the current bindings made at the prompt\n\
+\\n\
+\ :type <expr> show the type of <expr>\n\
+\ :undef <cmd> undefine user-defined command :<cmd>\n\
+\ :unset <option> ... unset options\n\
+\ :quit exit GHCi\n\
+\ :!<command> run the shell command <command>\n\
\\n\
-\ :type <expr> show the type of <expr>\n\
-\ :undef <cmd> undefine user-defined command :<cmd>\n\
-\ :unset <option> ... unset options\n\
-\ :quit exit GHCi\n\
-\ :!<command> run the shell command <command>\n\
-\\
\ Options for `:set' and `:unset':\n\
-\\
+\\n\
\ +r revert top-level expressions after each evaluation\n\
\ +s print timing/memory stats after each evaluation\n\
\ +t print type after evaluation\n\
_ -> panic "interactiveUI:buffering"
(cmstate, maybe_hval)
- <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stderr"
+ <- cmCompileExpr cmstate dflags "IO.hFlush IO.stderr"
case maybe_hval of
Just hval -> writeIORef flush_stderr (unsafeCoerce# hval :: IO ())
_ -> panic "interactiveUI:stderr"
(cmstate, maybe_hval)
- <- cmCompileExpr cmstate dflags "IO.hFlush PrelHandle.stdout"
+ <- cmCompileExpr cmstate dflags "IO.hFlush IO.stdout"
case maybe_hval of
Just hval -> writeIORef flush_stdout (unsafeCoerce# hval :: IO ())
_ -> panic "interactiveUI:stdout"
then readlineLoop
else fileLoop stdin False -- turn off prompt for non-TTY input
#else
- fileLoop stdin True
+ fileLoop stdin is_tty
#endif
checkPerms :: String -> IO Bool
checkPerms name =
- handle (\_ -> return False) $ do
#ifdef mingw32_TARGET_OS
- doesFileExist name
+ return True
#else
+ DriverUtil.handle (\_ -> return False) $ do
st <- getFileStatus name
me <- getRealUserID
if fileOwner st /= me then do
if quit then return () else stringLoop ss
mkPrompt toplevs exports
- = concat (intersperse " " (toplevs ++ map ('*':) exports)) ++ "> "
+ = concat (intersperse " " (map ('*':) toplevs ++ exports)) ++ "> "
#if HAVE_READLINE_HEADERS && HAVE_READLINE_LIBS
readlineLoop :: GHCi ()
setContextAfterLoad (m:_) = do
cmstate <- getCmState
b <- io (cmModuleIsInterpreted cmstate m)
- if b then setContext m else setContext ('*':m)
+ if b then setContext ('*':m) else setContext m
modulesLoadedMsg ok mods dflags =
when (verbosity dflags > 0) $ do
| otherwise = hsep (
punctuate comma (map text mods)) <> text "."
case ok of
- False ->
+ False ->
io (putStrLn (showSDoc (text "Failed, modules loaded: " <> mod_commas)))
- True ->
+ True ->
io (putStrLn (showSDoc (text "Ok, modules loaded: " <> mod_commas)))
browseCmd :: String -> GHCi ()
browseCmd m =
case words m of
- ['*':m] | looksLikeModuleName m -> browseModule m True
- [m] | looksLikeModuleName m -> browseModule m False
+ ['*':m] | looksLikeModuleName m -> browseModule m False
+ [m] | looksLikeModuleName m -> browseModule m True
_ -> throwDyn (CmdLineError "syntax: :browse <module>")
browseModule m exports_only = do
thingDecl thing@(ATyCon t) =
let rn_decl = ifaceTyThing thing in
case rn_decl of
- TyData { tcdCons = cons } ->
- rn_decl{ tcdCons = filter conIsVisible cons }
+ TyData { tcdCons = DataCons cons } ->
+ rn_decl{ tcdCons = DataCons (filter conIsVisible cons) }
other -> other
where
conIsVisible (ConDecl n _ _ _ _ _) = n `elem` thing_names
setCmState cms'
separate cmstate [] as bs = return (as,bs)
-separate cmstate (('*':m):ms) as bs = separate cmstate ms as (m:bs)
-separate cmstate (m:ms) as bs = do
+separate cmstate (('*':m):ms) as bs = do
b <- io (cmModuleIsInterpreted cmstate m)
if b then separate cmstate ms (m:as) bs
else throwDyn (CmdLineError ("module `" ++ m ++ "' is not interpreted"))
-
+separate cmstate (m:ms) as bs = separate cmstate ms as (m:bs)
+
prel = "Prelude"
setOptions wds =
do -- first, deal with the GHCi opts (+s, +t, etc.)
let (plus_opts, minus_opts) = partition isPlus wds
- mapM setOpt plus_opts
+ mapM_ setOpt plus_opts
-- now, the GHC flags
pkgs_before <- io (readIORef v_Packages)
then io (putStrLn ("unknown option: `" ++ head rest2 ++ "'"))
else do
- mapM unsetOpt plus_opts
+ mapM_ unsetOpt plus_opts
-- can't do GHC flags for now
if (not (null minus_opts))
new_pkg_info <- getPackageDetails new_pkgs
mapM_ (linkPackage dflags) (reverse new_pkg_info)
+ setContextAfterLoad []
+
-----------------------------------------------------------------------------
-- code for `:show'
unqual = cmGetPrintUnqual cms
showBinding b = putStrLn (showSDocForUser unqual (ppr (ifaceTyThing b)))
- io (mapM showBinding (cmGetBindings cms))
+ io (mapM_ showBinding (cmGetBindings cms))
return ()
-----------------------------------------------------------------------------
-- For dynamic objects only, try to find the object file in all the
-- directories specified in v_Library_Paths before giving up.
-type LibrarySpec
- = Either FilePath String
+data LibrarySpec = Object FilePath | DLL String
+#ifdef darwin_TARGET_OS
+ | Framework String
+#endif
+
+-- Packages that don't need loading, because the compiler shares them with
+-- the interpreted program.
+dont_load_these = [ "rts" ]
+
+-- Packages that are already linked into GHCi. For mingw32, we only
+-- skip gmp and rts, since std and after need to load the msvcrt.dll
+-- library which std depends on.
+loaded_in_ghci
+# ifndef mingw32_TARGET_OS
+ = [ "std", "concurrent", "posix", "text", "util" ]
+# else
+ = [ ]
+# endif
-showLS (Left nm) = "(static) " ++ nm
-showLS (Right nm) = "(dynamic) " ++ nm
+showLS (Object nm) = "(static) " ++ nm
+showLS (DLL nm) = "(dynamic) " ++ nm
+#ifdef darwin_TARGET_OS
+showLS (Framework nm) = "(framework) " ++ nm
+#endif
linkPackages :: DynFlags -> [LibrarySpec] -> [PackageConfig] -> IO ()
linkPackages dflags cmdline_lib_specs pkgs
if (null cmdline_lib_specs)
then return ()
else do maybePutStr dflags "final link ... "
+
ok <- resolveObjs
if ok then maybePutStrLn dflags "done."
else throwDyn (InstallationError
preloadLib dflags lib_paths lib_spec
= do maybePutStr dflags ("Loading object " ++ showLS lib_spec ++ " ... ")
case lib_spec of
- Left static_ish
+ Object static_ish
-> do b <- preload_static lib_paths static_ish
maybePutStrLn dflags (if b then "done."
else "not found")
- Right dll_unadorned
+ DLL dll_unadorned
-> -- We add "" to the set of paths to try, so that
-- if none of the real paths match, we force addDLL
-- to look in the default dynamic-link search paths.
- do maybe_errstr <- preload_dynamic (lib_paths++[""])
+ do maybe_errstr <- loadDynamic (lib_paths++[""])
dll_unadorned
case maybe_errstr of
Nothing -> return ()
if not b then return False
else loadObj name >> return True
- -- return Nothing == success, else Just error message from addDLL
- preload_dynamic [] name
- = return Nothing
- preload_dynamic (path:paths) rootname
- = do -- addDLL returns NULL on success
- maybe_errmsg <- addDLL path rootname
- if maybe_errmsg == nullPtr
- then preload_dynamic paths rootname
- else do str <- peekCString maybe_errmsg
- return (Just str)
-
give_up
= (throwDyn . CmdLineError)
"user specified .o/.so/.DLL could not be loaded."
--- Packages that don't need loading, because the compiler shares them with
--- the interpreted program.
-dont_load_these = [ "gmp", "rts" ]
-
--- Packages that are already linked into GHCi. For mingw32, we only
--- skip gmp and rts, since std and after need to load the msvcrt.dll
--- library which std depends on.
-loaded_in_ghci
-# ifndef mingw32_TARGET_OS
- = [ "std", "concurrent", "posix", "text", "util" ]
-# else
- = [ ]
-# endif
-
linkPackage :: DynFlags -> PackageConfig -> IO ()
linkPackage dflags pkg
| name pkg `elem` dont_load_these = return ()
| otherwise
= do
- -- For each obj, try obj.o and if that fails, obj.so.
- -- Complication: all the .so's must be loaded before any of the .o's.
let dirs = library_dirs pkg
- let objs = hs_libraries pkg ++ extra_libraries pkg
- classifieds <- mapM (locateOneObj dirs) objs
+ let libs = hs_libraries pkg ++ extra_libraries pkg
+ classifieds <- mapM (locateOneObj dirs) libs
+#ifdef darwin_TARGET_OS
+ let fwDirs = framework_dirs pkg
+ let frameworks= extra_frameworks pkg
+#endif
- -- Don't load the .so libs if this is a package GHCi is already
- -- linked against, because we'll already have the .so linked in.
- let (so_libs, obj_libs) = partition isRight classifieds
- let sos_first | name pkg `elem` loaded_in_ghci = obj_libs
- | otherwise = so_libs ++ obj_libs
+ -- Complication: all the .so's must be loaded before any of the .o's.
+ let dlls = [ dll | DLL dll <- classifieds ]
+ objs = [ obj | Object obj <- classifieds ]
maybePutStr dflags ("Loading package " ++ name pkg ++ " ... ")
- mapM loadClassified sos_first
+
+ -- If this package is already part of the GHCi binary, we'll already
+ -- have the right DLLs for this package loaded, so don't try to
+ -- load them again.
+ when (name pkg `notElem` loaded_in_ghci) $ do
+#ifdef darwin_TARGET_OS
+ loadFrameworks fwDirs frameworks
+#endif
+ loadDynamics dirs dlls
+
+ -- After loading all the DLLs, we can load the static objects.
+ mapM_ loadObj objs
+
maybePutStr dflags "linking ... "
ok <- resolveObjs
if ok then maybePutStrLn dflags "done."
else panic ("can't load package `" ++ name pkg ++ "'")
- where
- isRight (Right _) = True
- isRight (Left _) = False
-
-loadClassified :: LibrarySpec -> IO ()
-loadClassified (Left obj_absolute_filename)
- = do loadObj obj_absolute_filename
-loadClassified (Right dll_unadorned)
- = do maybe_errmsg <- addDLL "" dll_unadorned -- doesn't seem right to me
- if maybe_errmsg == nullPtr
- then return ()
- else do str <- peekCString maybe_errmsg
- throwDyn (CmdLineError ("can't load .so/.DLL for: "
- ++ dll_unadorned ++ " (" ++ str ++ ")" ))
+loadDynamics dirs [] = return ()
+loadDynamics dirs (dll:dlls) = do
+ r <- loadDynamic dirs dll
+ case r of
+ Nothing -> loadDynamics dirs dlls
+ Just err -> throwDyn (CmdLineError ("can't load .so/.DLL for: "
+ ++ dll ++ " (" ++ err ++ ")" ))
+#ifdef darwin_TARGET_OS
+loadFrameworks dirs [] = return ()
+loadFrameworks dirs (fw:fws) = do
+ r <- loadFramework dirs fw
+ case r of
+ Nothing -> loadFrameworks dirs fws
+ Just err -> throwDyn (CmdLineError ("can't load framework: "
+ ++ fw ++ " (" ++ err ++ ")" ))
+#endif
+
+-- Try to find an object file for a given library in the given paths.
+-- If it isn't present, we assume it's a dynamic library.
locateOneObj :: [FilePath] -> String -> IO LibrarySpec
-locateOneObj [] obj
- = return (Right obj) -- we assume
-locateOneObj (d:ds) obj
- = do let path = d ++ '/':obj ++ ".o"
+locateOneObj [] lib
+ = return (DLL lib) -- we assume
+locateOneObj (d:ds) lib
+ = do let path = d ++ '/':lib ++ ".o"
b <- doesFileExist path
- if b then return (Left path) else locateOneObj ds obj
+ if b then return (Object path) else locateOneObj ds lib
+
+-- ----------------------------------------------------------------------------
+-- Loading a dyanmic library (dlopen()-ish on Unix, LoadLibrary-ish on Win32)
+
+#if defined(mingw32_TARGET_OS) || defined(cygwin32_TARGET_OS)
+loadDynamic paths rootname = addDLL rootname
+ -- ignore paths on windows (why? --SDM)
+
+#else
+
+-- return Nothing == success, else Just error message from dlopen
+loadDynamic (path:paths) rootname = do
+ let dll = path ++ '/':mkSOName rootname
+ b <- doesFileExist dll
+ if not b
+ then loadDynamic paths rootname
+ else addDLL dll
+loadDynamic [] rootname = do
+ -- tried all our known library paths, let dlopen() search its
+ -- own builtin paths now.
+ addDLL (mkSOName rootname)
+
+#ifdef darwin_TARGET_OS
+mkSOName root = "lib" ++ root ++ ".dylib"
+#else
+mkSOName root = "lib" ++ root ++ ".so"
+#endif
+
+#endif
+
+-- Darwin / MacOS X only: load a framework
+-- a framework is a dynamic library packaged inside a directory of the same
+-- name. They are searched for in different paths than normal libraries.
+#ifdef darwin_TARGET_OS
+loadFramework extraPaths rootname
+ = loadFramework' (extraPaths ++ defaultFrameworkPaths) where
+ defaultFrameworkPaths = ["/Library/Frameworks", "/System/Library/Frameworks"]
+
+ loadFramework' (path:paths) = do
+ let dll = path ++ '/' : rootname ++ ".framework/" ++ rootname
+ b <- doesFileExist dll
+ if not b
+ then loadFramework' paths
+ else addDLL dll
+ loadFramework' [] = do
+ -- tried all our known library paths, but dlopen()
+ -- has no built-in paths for frameworks: give up
+ return $ Just $ "not found"
+#endif
+
+addDLL :: String -> IO (Maybe String)
+addDLL str = do
+ maybe_errmsg <- withCString str $ \dll -> c_addDLL dll
+ if maybe_errmsg == nullPtr
+ then return Nothing
+ else do str <- peekCString maybe_errmsg
+ return (Just str)
+
+foreign import ccall "addDLL" unsafe
+ c_addDLL :: CString -> IO CString
-----------------------------------------------------------------------------
-- timing & statistics
looksLikeModuleName [] = False
looksLikeModuleName (c:cs) = isUpper c && all isAlphaNumEx cs
-isAlphaNumEx c = isAlphaNum c || c == '_'
+isAlphaNumEx c = isAlphaNum c || c == '_' || c == '.'
maybePutStr dflags s | verbosity dflags > 0 = putStr s
| otherwise = return ()