[project @ 2000-11-20 16:28:29 by simonmar]
[ghc-hetmet.git] / ghc / compiler / compMan / CompManager.lhs
index b889c86..7f0885a 100644 (file)
@@ -4,71 +4,93 @@
 \section[CompManager]{The Compilation Manager}
 
 \begin{code}
-module CompManager ( cmInit, cmLoadModule, 
+module CompManager ( cmInit, cmLoadModule,
+#ifdef GHCI
                      cmGetExpr, cmRunExpr,
-                     CmState, emptyCmState,  -- abstract
-                    cmLookupSymbol --tmp
+#endif
+                     CmState, emptyCmState  -- abstract
                    )
 where
 
 #include "HsVersions.h"
 
-import List            ( nub )
-import Maybe           ( catMaybes, maybeToList, fromMaybe )
-import Maybes          ( maybeToBool )
-import Outputable
-import UniqFM          ( emptyUFM, lookupUFM, addToUFM, delListFromUFM,
-                         UniqFM, listToUFM )
-import Unique          ( Uniquable )
-import Digraph         ( SCC(..), stronglyConnComp, flattenSCC, flattenSCCs )
-
 import CmLink
 import CmTypes
 import HscTypes
-import Interpreter     ( HValue )
-import Module          ( ModuleName, moduleName, packageOfModule, 
-                         isModuleInThisPackage, PackageName, moduleEnvElts,
+import Module          ( ModuleName, moduleName,
+                         isModuleInThisPackage, moduleEnvElts,
                          moduleNameUserString )
-import CmStaticInfo    ( Package(..), PackageConfigInfo, GhciMode(..) )
+import CmStaticInfo    ( PackageConfigInfo, GhciMode(..) )
 import DriverPipeline
 import GetImports
 import HscTypes                ( HomeSymbolTable, HomeIfaceTable, 
                          PersistentCompilerState, ModDetails(..) )
 import Name            ( lookupNameEnv )
-import RdrName
 import Module
 import PrelNames       ( mainName )
 import HscMain         ( initPersistentCompilerState )
-import Finder          ( findModule, emptyHomeDirCache )
-import DriverUtil      ( BarfKind(..) )
+import Finder
+import UniqFM          ( emptyUFM, lookupUFM, addToUFM, delListFromUFM,
+                         UniqFM, listToUFM )
+import Unique          ( Uniquable )
+import Digraph         ( SCC(..), stronglyConnComp )
+import DriverPhases
+import DriverUtil      ( BarfKind(..), splitFilename3 )
 import Util
+import Outputable
 import Panic           ( panic )
 
+#ifdef GHCI
+import CmdLineOpts     ( DynFlags )
+import Interpreter     ( HValue )
+import HscMain         ( hscExpr )
+import RdrName
+import PrelGHC         ( unsafeCoerce# )
+#endif
+
+-- lang
 import Exception       ( throwDyn )
-import IO
+
+-- std
 import Time             ( ClockTime )
 import Directory        ( getModificationTime, doesFileExist )
-
+import IO
+import List            ( nub )
+import Maybe           ( catMaybes, fromMaybe, isJust )
 \end{code}
 
 
-
 \begin{code}
 cmInit :: PackageConfigInfo -> GhciMode -> IO CmState
 cmInit raw_package_info gmode
    = emptyCmState raw_package_info gmode
 
+#ifdef GHCI
 cmGetExpr :: CmState
+         -> DynFlags
           -> ModuleName
           -> String
-          -> IO (CmState, Either [SDoc] HValue)
-cmGetExpr cmstate modhdl expr
-   = return (panic "cmGetExpr:unimp")
+          -> IO (CmState, Maybe HValue)
+cmGetExpr cmstate dflags modname expr
+   = do (new_pcs, maybe_unlinked_iexpr) <- 
+          hscExpr dflags hst hit pcs (mkModuleInThisPackage modname) expr
+        case maybe_unlinked_iexpr of
+          Nothing     -> return (cmstate{ pcs=new_pcs }, Nothing)
+          Just uiexpr -> do
+               hValue <- linkExpr pls uiexpr
+               return (cmstate{ pcs=new_pcs }, Just hValue)
+
+   -- ToDo: check that the module we passed in is sane/exists?
+   where
+       CmState{ pcs=pcs, pcms=pcms, pls=pls } = cmstate
+       PersistentCMState{ hst=hst, hit=hit } = pcms
 
+-- The HValue should represent a value of type IO () (Perhaps IO a?)
 cmRunExpr :: HValue -> IO ()
 cmRunExpr hval
-   = return (panic "cmRunExpr:unimp")
-
+   = do unsafeCoerce# hval :: IO ()
+       -- putStrLn "done."
+#endif
 
 -- Persistent state just for CM, excluding link & compile subsystems
 data PersistentCMState
@@ -128,8 +150,10 @@ the system state at the same time.
 
 \begin{code}
 cmLoadModule :: CmState 
-             -> ModuleName
-             -> IO (CmState, Maybe ModuleName)
+             -> FilePath
+             -> IO (CmState,           -- new state
+                   Bool,               -- was successful
+                   [ModuleName])       -- list of modules loaded
 
 cmLoadModule cmstate1 rootname
    = do -- version 1's are the original, before downsweep
@@ -144,12 +168,6 @@ cmLoadModule cmstate1 rootname
         let pcii      = pci   pcms1 -- this never changes
         let ghci_mode = gmode pcms1 -- ToDo: fix!
 
-        -- During upsweep, look at new summaries to see if source has
-        -- changed.  Here's a function to pass down; it takes a new
-        -- summary.
-        let source_changed :: ModSummary -> Bool
-            source_changed = summary_indicates_source_changed mg1
-
         -- Do the downsweep to reestablish the module graph
         -- then generate version 2's by removing from HIT,HST,UI any
         -- modules in the old MG which are not in the new one.
@@ -191,7 +209,7 @@ cmLoadModule cmstate1 rootname
         let threaded2 = CmThreaded pcs1 hst2 hit2
 
         (upsweep_complete_success, threaded3, modsDone, newLis)
-           <- upsweep_mods ghci_mode ui2 reachable_from source_changed threaded2 mg2
+           <- upsweep_mods ghci_mode ui2 reachable_from threaded2 mg2
 
         let ui3 = add_to_ui ui2 newLis
         let (CmThreaded pcs3 hst3 hit3) = threaded3
@@ -220,7 +238,7 @@ cmLoadModule cmstate1 rootname
                                                           pci=pcii, gmode=ghci_mode }
                           let cmstate3 
                                  = CmState { pcms=pcms3, pcs=pcs3, pls=pls3 }
-                          return (cmstate3, Just rootname)
+                          return (cmstate3, True, map name_of_summary modsDone)
 
          else 
            -- Tricky.  We need to back out the effects of compiling any
@@ -241,7 +259,8 @@ cmLoadModule cmstate1 rootname
               -- we could get the relevant linkables by filtering newLis, but
               -- it seems easier to drag them out of the updated, cleaned-up UI
               let linkables_to_link 
-                     = map (findModuleLinkable ui4) mods_to_keep_names
+                     = map (unJust "linkables_to_link" . findModuleLinkable_maybe ui4)
+                           mods_to_keep_names
 
               linkresult <- link ghci_mode False linkables_to_link pls1
               case linkresult of
@@ -253,40 +272,8 @@ cmLoadModule cmstate1 rootname
                                                           pci=pcii, gmode=ghci_mode }
                           let cmstate4 
                                  = CmState { pcms=pcms4, pcs=pcs3, pls=pls4 }
-                          return (cmstate4, 
-                                  -- choose rather arbitrarily who to return
-                                  if null mods_to_keep then Nothing 
-                                     else Just (last mods_to_keep_names))
-
-
--- Given a bunch of old summaries and a new summary, try and
--- find the corresponding old summary, and, if found, compare
--- its source timestamp with that of the new summary.  If in
--- doubt say True.
-summary_indicates_source_changed :: [ModSummary] -> ModSummary -> Bool
-summary_indicates_source_changed old_summaries new_summary
-   = case [old | old <- old_summaries, 
-                 name_of_summary old == name_of_summary new_summary] of
-
-        (_:_:_) -> panic "summary_indicates_newer_source"
-                   
-        []      -> -- can't find a corresponding old summary, so
-                   -- compare source and iface dates in the new summary.
-                   trace (showSDoc (text "SISC: no old summary, new =" 
-                                    <+> pprSummaryTimes new_summary)) (
-                   case (ms_hs_date new_summary, ms_hi_date new_summary) of
-                      (Just hs_t, Just hi_t) -> hs_t > hi_t
-                      other                  -> True
-                   )
+                          return (cmstate4, False, mods_to_keep_names)
 
-        [old]   -> -- found old summary; compare source timestamps
-                   trace (showSDoc (text "SISC: old =" 
-                                    <+> pprSummaryTimes old
-                                    <+> pprSummaryTimes new_summary)) (
-                   case (ms_hs_date old, ms_hs_date new_summary) of
-                      (Just old_t, Just new_t) -> new_t > old_t
-                      other                    -> True
-                   )
 
 -- Return (names of) all those in modsDone who are part of a cycle
 -- as defined by theGraph.
@@ -312,7 +299,7 @@ findPartiallyCompletedCycles modsDone theGraph
 -- Does this ModDetails export Main.main?
 exports_main :: ModDetails -> Bool
 exports_main md
-   = maybeToBool (lookupNameEnv (md_types md) mainName)
+   = isJust (lookupNameEnv (md_types md) mainName)
 
 
 -- Add the given (LM-form) Linkables to the UI, overwriting previous
@@ -341,7 +328,6 @@ data CmThreaded  -- stuff threaded through individual module compilations
 upsweep_mods :: GhciMode
              -> UnlinkedImage         -- old linkables
              -> (ModuleName -> [ModuleName])  -- to construct downward closures
-             -> (ModSummary -> Bool)  -- has source changed?
              -> CmThreaded            -- PCS & HST & HIT
              -> [SCC ModSummary]      -- mods to do (the worklist)
                                       -- ...... RETURNING ......
@@ -350,24 +336,27 @@ upsweep_mods :: GhciMode
                     [ModSummary],     -- mods which succeeded
                     [Linkable])       -- new linkables
 
-upsweep_mods ghci_mode oldUI reachable_from source_changed threaded []
+upsweep_mods ghci_mode oldUI reachable_from threaded 
+     []
    = return (True, threaded, [], [])
 
-upsweep_mods ghci_mode oldUI reachable_from source_changed threaded ((CyclicSCC ms):_)
+upsweep_mods ghci_mode oldUI reachable_from threaded 
+     ((CyclicSCC ms):_)
    = do hPutStrLn stderr ("ghc: module imports form a cycle for modules:\n\t" ++
                           unwords (map (moduleNameUserString.name_of_summary) ms))
         return (False, threaded, [], [])
 
-upsweep_mods ghci_mode oldUI reachable_from source_changed threaded ((AcyclicSCC mod):mods)
+upsweep_mods ghci_mode oldUI reachable_from threaded 
+     ((AcyclicSCC mod):mods)
    = do (threaded1, maybe_linkable) 
            <- upsweep_mod ghci_mode oldUI threaded mod 
                           (reachable_from (name_of_summary mod)) 
-                          (source_changed mod)
         case maybe_linkable of
            Just linkable 
               -> -- No errors; do the rest
                  do (restOK, threaded2, modOKs, linkables) 
-                       <- upsweep_mods ghci_mode oldUI reachable_from source_changed threaded1 mods
+                       <- upsweep_mods ghci_mode oldUI reachable_from 
+                                       threaded1 mods
                     return (restOK, threaded2, mod:modOKs, linkable:linkables)
            Nothing -- we got a compilation error; give up now
               -> return (False, threaded1, [], [])
@@ -375,16 +364,29 @@ upsweep_mods ghci_mode oldUI reachable_from source_changed threaded ((AcyclicSCC
 
 -- Compile a single module.  Always produce a Linkable for it if 
 -- successful.  If no compilation happened, return the old Linkable.
+maybe_getFileLinkable :: ModuleName -> FilePath -> IO (Maybe Linkable)
+maybe_getFileLinkable mod_name obj_fn
+   = do obj_exist <- doesFileExist obj_fn
+        if not obj_exist 
+         then return Nothing 
+         else 
+         do let stub_fn = case splitFilename3 obj_fn of
+                             (dir, base, ext) -> dir ++ "/" ++ base ++ ".stub_o"
+            stub_exist <- doesFileExist stub_fn
+            obj_time <- getModificationTime obj_fn
+            if stub_exist
+             then return (Just (LM obj_time mod_name [DotO obj_fn, DotO stub_fn]))
+             else return (Just (LM obj_time mod_name [DotO obj_fn]))
+
+
 upsweep_mod :: GhciMode 
             -> UnlinkedImage
             -> CmThreaded
             -> ModSummary
             -> [ModuleName]
-            -> Bool
             -> IO (CmThreaded, Maybe Linkable)
 
-upsweep_mod ghci_mode oldUI threaded1 summary1 
-            reachable_from_here source_might_have_changed
+upsweep_mod ghci_mode oldUI threaded1 summary1 reachable_from_here
    = do let mod_name = name_of_summary summary1
         let (CmThreaded pcs1 hst1 hit1) = threaded1
         let old_iface = lookupUFM hit1 (name_of_summary summary1)
@@ -400,19 +402,42 @@ upsweep_mod ghci_mode oldUI threaded1 summary1
                                         b <- doesFileExist obj_fn
                                         return (not b)
 
-        let compilation_might_be_needed 
-               = source_might_have_changed || compilation_mandatory
+        let maybe_oldUI_linkable = findModuleLinkable_maybe oldUI mod_name
+        maybe_oldDisk_linkable
+           <- case ml_obj_file (ms_location summary1) of
+                 Nothing -> return Nothing
+                 Just obj_fn -> maybe_getFileLinkable mod_name obj_fn
+
+        -- The most recent of the old UI linkable or whatever we could
+        -- find on disk.  Is returned as the linkable if compile
+        -- doesn't think we need to recompile.        
+        let maybe_old_linkable
+               = case (maybe_oldUI_linkable, maybe_oldDisk_linkable) of
+                    (Nothing, Nothing) -> Nothing
+                    (Nothing, Just di) -> Just di
+                    (Just ui, Nothing) -> Just ui
+                    (Just ui, Just di)
+                       | linkableTime ui >= linkableTime di -> Just ui
+                       | otherwise                          -> Just di
+
+        let compilation_mandatory
+               = case maybe_old_linkable of
+                    Nothing -> True
+                    Just li -> case ms_hs_date summary1 of
+                                  Nothing -> panic "compilation_mandatory:no src date"
+                                  Just src_date -> src_date >= linkableTime li
             source_unchanged
-               = not compilation_might_be_needed
+               = not compilation_mandatory
+
             (hst1_strictDC, hit1_strictDC)
                = retainInTopLevelEnvs reachable_from_here (hst1,hit1)
 
+            old_linkable 
+               = unJust "upsweep_mod:old_linkable" maybe_old_linkable
+
         compresult <- compile ghci_mode summary1 source_unchanged
                          old_iface hst1_strictDC hit1_strictDC pcs1
 
-        --putStrLn ( "UPSWEEP_MOD: smhc = " ++ show source_might_have_changed 
-        --           ++ ",  cman = " ++ show compilation_mandatory)
-
         case compresult of
 
            -- Compilation "succeeded", but didn't return a new iface or
@@ -422,13 +447,6 @@ upsweep_mod ghci_mode oldUI threaded1 summary1
               -> let hst2         = addToUFM hst1 mod_name details
                      hit2         = hit1
                      threaded2    = CmThreaded pcs2 hst2 hit2
-                     old_linkable 
-                        | ghci_mode == Interactive 
-                        = findModuleLinkable oldUI mod_name
-                        | otherwise
-                        = LM mod_name
-                             [DotO (unJust (ml_obj_file (ms_location summary1)) 
-                                    "upsweep_mod")]
                  in  return (threaded2, Just old_linkable)
 
            -- Compilation really did happen, and succeeded.  A new
@@ -524,16 +542,31 @@ topological_sort include_source_imports summaries
 -- Chase downwards from the specified root set, returning summaries
 -- for all home modules encountered.  Only follow source-import
 -- links.
-downsweep :: [ModuleName] -> IO [ModSummary]
+downsweep :: [FilePath] -> IO [ModSummary]
 downsweep rootNm
-   = do rootSummaries <- mapM getSummary rootNm
+   = do rootSummaries <- mapM getRootSummary rootNm
         loop (filter (isModuleInThisPackage.ms_mod) rootSummaries)
      where
+       getRootSummary :: FilePath -> IO ModSummary
+       getRootSummary file
+          | haskellish_file file
+           = do exists <- doesFileExist file
+               if exists then summariseFile file
+                         else getSummary (mkModuleName file)
+               -- ToDo: should check import paths
+          | otherwise
+          = getSummary (mkModuleName file)
+
         getSummary :: ModuleName -> IO ModSummary
         getSummary nm
            | trace ("getSummary: "++ showSDoc (ppr nm)) True
            = do found <- findModule nm
                case found of
+                   -- Be sure not to use the mod and location passed in to 
+                   -- summarise for any other purpose -- summarise may change
+                   -- the module names in them if name of module /= name of file,
+                   -- and put the changed versions in the returned summary.
+                   -- These will then conflict with the passed-in versions.
                   Just (mod, location) -> summarise mod location
                   Nothing -> throwDyn (OtherError 
                                    ("no signs of life for module `" 
@@ -557,38 +590,72 @@ downsweep rootNm
                  else loop (newHomeSummaries ++ homeSummaries)
 
 
--- Summarise a module, and pick and source and interface timestamps.
+-----------------------------------------------------------------------------
+-- Summarising modules
+
+-- We have two types of summarisation:
+--
+--    * Summarise a file.  This is used for the root module passed to
+--     cmLoadModule.  The file is read, and used to determine the root
+--     module name.  The module name may differ from the filename.
+--
+--    * Summarise a module.  We are given a module name, and must provide
+--     a summary.  The finder is used to locate the file in which the module
+--     resides.
+
+summariseFile :: FilePath -> IO ModSummary
+summariseFile file
+   = do hspp_fn <- preprocess file
+        modsrc <- readFile hspp_fn
+
+        let (srcimps,imps,mod_name) = getImports modsrc
+           (path, basename, ext) = splitFilename3 file
+
+       Just (mod, location)
+          <- mkHomeModuleLocn mod_name (path ++ '/':basename) file
+          
+        maybe_src_timestamp
+           <- case ml_hs_file location of 
+                 Nothing     -> return Nothing
+                 Just src_fn -> maybe_getModificationTime src_fn
+
+        return (ModSummary mod
+                           location{ml_hspp_file=Just hspp_fn}
+                           srcimps imps
+                           maybe_src_timestamp)
+
+-- Summarise a module, and pick up source and interface timestamps.
 summarise :: Module -> ModuleLocation -> IO ModSummary
 summarise mod location
    | isModuleInThisPackage mod
-   = do let hs_fn = unJust (ml_hs_file location) "summarise"
+   = do let hs_fn = unJust "summarise" (ml_hs_file location)
         hspp_fn <- preprocess hs_fn
         modsrc <- readFile hspp_fn
-        let (srcimps,imps) = getImports modsrc
+        let (srcimps,imps,mod_name) = getImports modsrc
 
         maybe_src_timestamp
            <- case ml_hs_file location of 
                  Nothing     -> return Nothing
                  Just src_fn -> maybe_getModificationTime src_fn
-        maybe_iface_timestamp
-           <- case ml_hi_file location of 
-                 Nothing     -> return Nothing
-                 Just if_fn  -> maybe_getModificationTime if_fn
+
+       if mod_name == moduleName mod
+               then return ()
+               else throwDyn (OtherError 
+                       (showSDoc (text "file name does not match module name: "
+                          <+> ppr (moduleName mod) <+> text "vs" 
+                          <+> ppr mod_name)))
 
         return (ModSummary mod location{ml_hspp_file=Just hspp_fn} 
                                srcimps imps
-                               maybe_src_timestamp maybe_iface_timestamp)
-   | otherwise
-   = return (ModSummary mod location [] [] Nothing Nothing)
+                               maybe_src_timestamp)
 
-   where
-      maybe_getModificationTime :: FilePath -> IO (Maybe ClockTime)
-      maybe_getModificationTime fn
-         = (do time <- getModificationTime fn
-               return (Just time)) 
-           `catch`
-           (\err -> return Nothing)
-
-cmLookupSymbol :: RdrName -> CmState -> Maybe HValue
-cmLookupSymbol nm CmState{ pls = pls } = lookupClosure nm pls
+   | otherwise
+   = return (ModSummary mod location [] [] Nothing)
+
+maybe_getModificationTime :: FilePath -> IO (Maybe ClockTime)
+maybe_getModificationTime fn
+   = (do time <- getModificationTime fn
+         return (Just time)) 
+     `catch`
+     (\err -> return Nothing)
 \end{code}