[project @ 2005-02-14 16:49:50 by simonmar]
[ghc-hetmet.git] / ghc / compiler / main / Finder.lhs
index 24936ec..260ee07 100644 (file)
@@ -6,20 +6,16 @@
 \begin{code}
 module Finder (
     flushFinderCache,  -- :: IO ()
-
-    findModule,                -- :: ModuleName 
-                       --   -> IO (Either [FilePath] (Module, ModLocation))
-
-    findPackageModule,  -- :: ModuleName
-                       --   -> IO (Either [FilePath] (Module, ModLocation))
-
-    mkHomeModLocation, -- :: ModuleName -> FilePath -> IO ModLocation
+    FindResult(..),
+    findModule,                        -- :: ModuleName -> Bool -> IO FindResult
+    findPackageModule,         -- :: ModuleName -> Bool -> IO FindResult
+    mkHomeModLocation,         -- :: ModuleName -> FilePath -> IO ModLocation
+    mkHomeModLocation2,                -- :: ModuleName -> FilePath -> String -> IO ModLocation
+    addHomeModuleToFinder,     -- :: Module -> ModLocation -> IO ()
 
     findLinkable,      -- :: ModuleName -> ModLocation -> IO (Maybe Linkable)
 
-    hiBootExt,         -- :: String
-    hiBootVerExt,      -- :: String
-
+    cantFindError,     -- :: DynFlags -> Module -> FindResult -> SDoc
   ) where
 
 #include "HsVersions.h"
@@ -27,52 +23,60 @@ module Finder (
 import Module
 import UniqFM          ( filterUFM )
 import HscTypes                ( Linkable(..), Unlinked(..) )
+import Packages
 import DriverState
 import DriverUtil
 import FastString
-import Config
 import Util
+import CmdLineOpts     ( DynFlags(..) )
+import Outputable
 
 import DATA_IOREF      ( IORef, writeIORef, readIORef )
 
-import List
-import Directory
-import IO
-import Monad
+import Data.List
+import System.Directory
+import System.IO
+import Control.Monad
+import Maybes          ( MaybeErr(..) )
+import Data.Maybe      ( isNothing )
+
+
+type FileExt = String  -- Filename extension
+type BaseName = String -- Basename of file
 
 -- -----------------------------------------------------------------------------
 -- The Finder
 
--- The Finder provides a thin filesystem abstraction to the rest of the
--- compiler.  For a given module, it knows (a) whether the module lives
--- in the home package or in another package, so it can make a Module
--- from a ModuleName, and (b) where the source, interface, and object
--- files for a module live.
+-- The Finder provides a thin filesystem abstraction to the rest of
+-- the compiler.  For a given module, it can tell you where the
+-- source, interface, and object files for that module live.
 -- 
--- It does *not* know which particular package a module lives in, because
--- that information is only contained in the interface file.
+-- It does *not* know which particular package a module lives in.  Use
+-- Packages.moduleToPackageConfig for that.
 
 -- -----------------------------------------------------------------------------
 -- The finder's cache
 
-GLOBAL_VAR(finder_cache, emptyModuleEnv, ModuleEnv (Module,ModLocation))
+GLOBAL_VAR(finder_cache, emptyModuleEnv, ModuleEnv FinderCacheEntry)
+
+type FinderCacheEntry = (ModLocation, Maybe (PackageConfig,Bool))
 
 -- remove all the home modules from the cache; package modules are
 -- assumed to not move around during a session.
 flushFinderCache :: IO ()
 flushFinderCache = do
   fm <- readIORef finder_cache
-  writeIORef finder_cache (filterUFM (not . isHomeModule . fst) fm)
+  writeIORef finder_cache (filterUFM (\(loc,m) -> isNothing m) fm)
 
-addToFinderCache :: ModuleName -> (Module,ModLocation) -> IO ()
-addToFinderCache mod_name stuff = do
+addToFinderCache :: Module -> FinderCacheEntry -> IO ()
+addToFinderCache mod_name entry = do
   fm <- readIORef finder_cache
-  writeIORef finder_cache (extendModuleEnvByName fm mod_name stuff)
+  writeIORef finder_cache (extendModuleEnv fm mod_name entry)
 
-lookupFinderCache :: ModuleName -> IO (Maybe (Module,ModLocation))
+lookupFinderCache :: Module -> IO (Maybe FinderCacheEntry)
 lookupFinderCache mod_name = do
   fm <- readIORef finder_cache
-  return $! lookupModuleEnvByName fm mod_name
+  return $! lookupModuleEnv fm mod_name
 
 -- -----------------------------------------------------------------------------
 -- Locating modules
@@ -87,164 +91,203 @@ lookupFinderCache mod_name = do
 -- The ModLocation contains the names of all the files associated with
 -- that module: its source file, .hi file, object file, etc.
 
--- Returns: 
---     Right (Module, ModLocation)   if the module was found
---     Left [FilePath]               if the module was not found, and here
---                                     is a list of all the places we looked
-findModule :: ModuleName -> IO (Either [FilePath] (Module, ModLocation))
-findModule name = do
-  r <- lookupFinderCache name
-  case r of
-   Just result -> return (Right result)
-   Nothing -> do  
-       j <- maybeHomeModule name
-       case j of
-        Right home_module -> return (Right home_module)
-        Left home_files   -> do
-           r <- findPackageMod name
-           case r of
-               Right pkg_module -> return (Right pkg_module)
-               Left pkg_files   -> return (Left (home_files ++ pkg_files))
-
-findPackageModule :: ModuleName -> IO (Either [FilePath] (Module, ModLocation))
-findPackageModule name = do
-  r <- lookupFinderCache name
-  case r of
-   Just result -> return (Right result)
-   Nothing     -> findPackageMod name
-
-hiBootExt = "hi-boot"
-hiBootVerExt = "hi-boot-" ++ cHscIfaceFileVersion
-
-maybeHomeModule :: ModuleName -> IO (Either [FilePath] (Module, ModLocation))
-maybeHomeModule mod_name = do
-   home_path <- readIORef v_Import_paths
+data FindResult
+  = Found ModLocation PackageIdH
+       -- the module was found
+  | PackageHidden PackageId
+       -- for an explicit source import: the package containing the module is
+       -- not exposed.
+  | ModuleHidden  PackageId
+       -- for an explicit source import: the package containing the module is
+       -- exposed, but the module itself is hidden.
+  | NotFound [FilePath]
+       -- the module was not found, the specified places were searched.
+
+type LocalFindResult = MaybeErr [FilePath] FinderCacheEntry
+       -- LocalFindResult is used for internal functions which 
+       -- return a more informative type; it's munged into
+       -- the external FindResult by 'cached'
+
+cached :: (DynFlags -> Module -> IO LocalFindResult)
+       -> DynFlags -> Module -> Bool -> IO FindResult
+cached wrapped_fn dflags name explicit 
+  = do {       -- First try the cache
+         mb_entry <- lookupFinderCache name
+       ; case mb_entry of {
+           Just old_entry -> return (found old_entry) ;
+           Nothing    -> do
+
+       {       -- Now try the wrapped function
+         mb_entry <- wrapped_fn dflags name
+       ; case mb_entry of
+           Failed paths        -> return (NotFound paths)
+           Succeeded new_entry -> do { addToFinderCache name new_entry
+                                     ; return (found new_entry) }
+       }}} 
+  where
+       -- We've found the module, so the remaining question is
+       -- whether it's visible or not
+    found :: FinderCacheEntry -> FindResult
+    found (loc, Nothing)               = Found loc HomePackage
+    found (loc, Just (pkg, exposed_mod))
+       | explicit && not exposed_mod   = ModuleHidden pkg_name
+       | explicit && not (exposed pkg) = PackageHidden pkg_name
+       | otherwise                     = Found loc (ExtPackage (mkPackageId (package pkg)))
+       where
+         pkg_name = packageConfigId pkg
+
+addHomeModuleToFinder :: Module -> ModLocation -> IO ()
+addHomeModuleToFinder mod loc = addToFinderCache mod (loc, Nothing)
+
+
+-- -----------------------------------------------------------------------------
+--     The two external entry points
+
+
+findModule :: DynFlags -> Module -> Bool -> IO FindResult
+findModule = cached findModule' 
+  
+findPackageModule :: DynFlags -> Module -> Bool -> IO FindResult
+findPackageModule = cached findPackageModule'
+
+-- -----------------------------------------------------------------------------
+--     The internal workers
+
+findModule' :: DynFlags -> Module -> IO LocalFindResult
+-- Find home or package module
+findModule' dflags name = do
+    r <- findPackageModule' dflags name
+    case r of
+       Failed pkg_files -> do
+          j <- findHomeModule' dflags name
+          case j of
+               Failed home_files -> 
+                       return (Failed (home_files ++ pkg_files))
+               other_result
+                       -> return other_result
+       other_result
+               -> return other_result
+
+findHomeModule' :: DynFlags -> Module -> IO LocalFindResult
+findHomeModule' dflags mod = do
+   let home_path = importPaths dflags
    hisuf     <- readIORef v_Hi_suf
    mode      <- readIORef v_GhcMode
 
    let
      source_exts = 
-      [ ("hs",   mkHomeModLocationSearched mod_name)
-      , ("lhs",  mkHomeModLocationSearched mod_name)
+      [ ("hs",   mkHomeModLocationSearched mod "hs")
+      , ("lhs",  mkHomeModLocationSearched mod "lhs")
       ]
      
-     hi_exts = [ (hisuf,  mkHiOnlyModLocation hisuf mod_name) ]
+     hi_exts = [ (hisuf,               mkHiOnlyModLocation hisuf)
+              , (addBootSuffix hisuf,  mkHiOnlyModLocation hisuf)
+              ]
      
-     boot_exts =
-       [ (hiBootVerExt, mkHiOnlyModLocation hisuf mod_name)
-       , (hiBootExt,    mkHiOnlyModLocation hisuf mod_name)
-       ]
-
        -- In compilation manager modes, we look for source files in the home
        -- package because we can compile these automatically.  In one-shot
        -- compilation mode we look for .hi and .hi-boot files only.
-       --
-       -- When generating dependencies, we're interested in either category.
-       --
      exts
-         | mode == DoMkDependHS   = hi_exts ++ source_exts ++ boot_exts
+         | DoMkDependHS <- mode   = source_exts
          | isCompManagerMode mode = source_exts
-        | otherwise {-one-shot-} = hi_exts ++ boot_exts
+        | otherwise {-one-shot-} = hi_exts
 
-   searchPathExts home_path mod_name exts
+   searchPathExts home_path mod exts
        
--- -----------------------------------------------------------------------------
--- Looking for a package module
-
-findPackageMod :: ModuleName -> IO (Either [FilePath] (Module, ModLocation))
-findPackageMod mod_name = do
-  mode     <- readIORef v_GhcMode
-  imp_dirs <- getPackageImportPath -- including the 'auto' ones
-
-   -- hi-suffix for packages depends on the build tag.
-  package_hisuf <-
-       do tag <- readIORef v_Build_tag
-          if null tag
-               then return "hi"
-               else return (tag ++ "_hi")
-
+findPackageModule' :: DynFlags -> Module -> IO LocalFindResult
+findPackageModule' dflags mod 
+  = case moduleToPackageConfig dflags mod of
+       Nothing       -> return (Failed [])
+       Just pkg_info -> findPackageIface mod pkg_info
+
+findPackageIface :: Module -> (PackageConfig,Bool) -> IO LocalFindResult
+findPackageIface mod pkg_info@(pkg_conf, _) = do
+  mode <- readIORef v_GhcMode
+  tag  <- readIORef v_Build_tag
   let
+          -- hi-suffix for packages depends on the build tag.
+     package_hisuf | null tag  = "hi"
+                  | otherwise = tag ++ "_hi"
      hi_exts =
-        [ (package_hisuf, mkPackageModLocation package_hisuf mod_name) ]
+        [ (package_hisuf, 
+           mkPackageModLocation pkg_info package_hisuf) ]
 
      source_exts = 
-       [ ("hs",   mkPackageModLocation package_hisuf mod_name)
-       , ("lhs",  mkPackageModLocation package_hisuf mod_name)
+       [ ("hs",   mkPackageModLocation pkg_info package_hisuf)
+       , ("lhs",  mkPackageModLocation pkg_info package_hisuf)
        ]
-     
+
      -- mkdependHS needs to look for source files in packages too, so
      -- that we can make dependencies between package before they have
      -- been built.
      exts 
-      | mode == DoMkDependHS = hi_exts ++ source_exts
-      | otherwise = hi_exts
-
+      | DoMkDependHS <- mode = hi_exts ++ source_exts
+      | otherwise           = hi_exts
       -- we never look for a .hi-boot file in an external package;
       -- .hi-boot files only make sense for the home package.
-  searchPathExts imp_dirs mod_name exts
+
+  searchPathExts (importDirs pkg_conf) mod exts
 
 -- -----------------------------------------------------------------------------
 -- General path searching
 
 searchPathExts
   :: [FilePath]                -- paths to search
-  -> ModuleName                -- module name
+  -> Module            -- module name
   -> [ (
-       String,                                         -- suffix
-       String -> String -> String -> IO (Module, ModLocation)  -- action
+       FileExt,                                     -- suffix
+       FilePath -> BaseName -> IO FinderCacheEntry  -- action
        )
      ] 
-  -> IO (Either [FilePath] (Module, ModLocation))
+  -> IO LocalFindResult
+
+searchPathExts paths mod exts 
+   = do result <- search to_search
+{-
+       hPutStrLn stderr (showSDoc $
+               vcat [text "Search" <+> ppr mod <+> sep (map (text. fst) exts)
+                   , nest 2 (vcat (map text paths))
+                   , case result of
+                       Succeeded (loc, p) -> text "Found" <+> ppr loc
+                       Failed fs          -> text "not found"])
+-}     
+       return result
 
-searchPathExts path mod_name exts = search to_search
   where
-    basename = dots_to_slashes (moduleNameUserString mod_name)
+    basename = dots_to_slashes (moduleUserString mod)
 
-    to_search :: [(FilePath, IO (Module,ModLocation))]
-    to_search = [ (file, fn p basename ext)
-               | p <- path, 
+    to_search :: [(FilePath, IO FinderCacheEntry)]
+    to_search = [ (file, fn path basename)
+               | path <- paths, 
                  (ext,fn) <- exts,
-                 let base | p == "."  = basename
-                          | otherwise = p ++ '/':basename
+                 let base | path == "." = basename
+                          | otherwise   = path ++ '/':basename
                      file = base ++ '.':ext
                ]
 
-    search [] = return (Left (map fst to_search))
-    search ((file, result) : rest) = do
+    search [] = return (Failed (map fst to_search))
+    search ((file, mk_result) : rest) = do
       b <- doesFileExist file
       if b 
-       then Right `liftM` result
+       then do { res <- mk_result; return (Succeeded res) }
        else search rest
 
--- -----------------------------------------------------------------------------
--- Building ModLocations
+mkHomeModLocationSearched :: Module -> FileExt
+                         -> FilePath -> BaseName -> IO FinderCacheEntry
+mkHomeModLocationSearched mod suff path basename = do
+   loc <- mkHomeModLocation2 mod (path ++ '/':basename) suff
+   return (loc, Nothing)
 
-mkHiOnlyModLocation hisuf mod_name path basename _ext = do
-  -- basename == dots_to_slashes (moduleNameUserString mod_name)
+mkHiOnlyModLocation :: FileExt -> FilePath -> BaseName -> IO FinderCacheEntry
+mkHiOnlyModLocation hisuf path basename = do
   loc <- hiOnlyModLocation path basename hisuf
-  let result = (mkHomeModule mod_name, loc)
-  addToFinderCache mod_name result
-  return result
+  return (loc, Nothing)
 
-mkPackageModLocation hisuf mod_name path basename _ext = do
-  -- basename == dots_to_slashes (moduleNameUserString mod_name)
+mkPackageModLocation :: (PackageConfig, Bool) -> FileExt
+                    -> FilePath -> BaseName -> IO FinderCacheEntry
+mkPackageModLocation pkg_info hisuf path basename = do
   loc <- hiOnlyModLocation path basename hisuf
-  let result = (mkPackageModule mod_name, loc)
-  addToFinderCache mod_name result
-  return result
-
-hiOnlyModLocation path basename hisuf 
- = do let full_basename = path++'/':basename
-      obj_fn <- mkObjPath full_basename basename
-      return ModLocation{ ml_hspp_file = Nothing,
-                            ml_hs_file   = Nothing,
-                            ml_hi_file   = full_basename ++ '.':hisuf,
-                               -- Remove the .hi-boot suffix from
-                               -- hi_file, if it had one.  We always
-                               -- want the name of the real .hi file
-                               -- in the ml_hi_file field.
-                            ml_obj_file  = obj_fn
-                  }
+  return (loc, Just pkg_info)
 
 -- -----------------------------------------------------------------------------
 -- Constructing a home module location
@@ -265,7 +308,7 @@ hiOnlyModLocation path basename hisuf
 --
 -- Parameters are:
 --
--- mod_name
+-- mod
 --      The name of the module
 --
 -- path
@@ -273,34 +316,43 @@ hiOnlyModLocation path basename hisuf
 --      (b) and (c): "."
 --
 -- src_basename
---      (a): dots_to_slashes (moduleNameUserString mod_name)
+--      (a): dots_to_slashes (moduleNameUserString mod)
 --      (b) and (c): The filename of the source file, minus its extension
 --
 -- ext
 --     The filename extension of the source file (usually "hs" or "lhs").
 
-mkHomeModLocation mod_name src_filename = do
+mkHomeModLocation :: Module -> FilePath -> IO ModLocation
+mkHomeModLocation mod src_filename = do
    let (basename,extension) = splitFilename src_filename
-   mkHomeModLocation' mod_name basename extension
+   mkHomeModLocation2 mod basename extension
 
-mkHomeModLocationSearched mod_name path basename ext =
-   mkHomeModLocation' mod_name (path ++ '/':basename) ext
-
-mkHomeModLocation' mod_name src_basename ext = do
-   let mod_basename = dots_to_slashes (moduleNameUserString mod_name)
+mkHomeModLocation2 :: Module   
+                  -> FilePath  -- Of source module, without suffix
+                  -> String    -- Suffix
+                  -> IO ModLocation
+mkHomeModLocation2 mod src_basename ext = do
+   let mod_basename = dots_to_slashes (moduleUserString mod)
 
    obj_fn <- mkObjPath src_basename mod_basename
    hi_fn  <- mkHiPath  src_basename mod_basename
 
-   let result = ( mkHomeModule mod_name,
-                  ModLocation{ ml_hspp_file = Nothing,
-                               ml_hs_file   = Just (src_basename ++ '.':ext),
-                               ml_hi_file   = hi_fn,
-                               ml_obj_file  = obj_fn
-                      })
+   return (ModLocation{ ml_hs_file   = Just (src_basename ++ '.':ext),
+                       ml_hi_file   = hi_fn,
+                       ml_obj_file  = obj_fn })
 
-   addToFinderCache mod_name result
-   return result
+hiOnlyModLocation :: FilePath -> String -> Suffix -> IO ModLocation
+hiOnlyModLocation path basename hisuf 
+ = do let full_basename = path++'/':basename
+      obj_fn <- mkObjPath full_basename basename
+      return ModLocation{    ml_hs_file   = Nothing,
+                            ml_hi_file   = full_basename ++ '.':hisuf,
+                               -- Remove the .hi-boot suffix from
+                               -- hi_file, if it had one.  We always
+                               -- want the name of the real .hi file
+                               -- in the ml_hi_file field.
+                            ml_obj_file  = obj_fn
+                  }
 
 -- | Constructs the filename of a .o file for a given source file.
 -- Does /not/ check whether the .o file exists
@@ -332,11 +384,12 @@ mkHiPath basename mod_basename
 
         return (hi_basename ++ '.':hisuf)
 
+
 -- -----------------------------------------------------------------------------
 -- findLinkable isn't related to the other stuff in here, 
 -- but there's no other obvious place for it
 
-findLinkable :: ModuleName -> ModLocation -> IO (Maybe Linkable)
+findLinkable :: Module -> ModLocation -> IO (Maybe Linkable)
 findLinkable mod locn
    = do let obj_fn = ml_obj_file locn
        obj_exist <- doesFileExist obj_fn
@@ -356,4 +409,33 @@ findLinkable mod locn
 
 dots_to_slashes = map (\c -> if c == '.' then '/' else c)
 
+
+-- -----------------------------------------------------------------------------
+-- Error messages
+
+cantFindError :: DynFlags -> Module -> FindResult -> SDoc
+cantFindError dflags mod_name find_result
+  = hang (ptext SLIT("Could not find module") <+> quotes (ppr mod_name) <> colon)
+       2 more_info
+  where
+    more_info
+      = case find_result of
+           PackageHidden pkg 
+               -> ptext SLIT("it is a member of package") <+> ppr pkg <> comma
+                  <+> ptext SLIT("which is hidden")
+
+           ModuleHidden pkg
+               -> ptext SLIT("it is hidden") <+> parens (ptext SLIT("in package")
+                  <+> ppr pkg)
+
+           NotFound files
+               | null files
+               -> ptext SLIT("it is not a module in the current program, or in any known package.")
+               | verbosity dflags < 3 
+               -> ptext SLIT("use -v to see a list of the files searched for")
+               | otherwise 
+               -> hang (ptext SLIT("locations searched:")) 
+                     2 (vcat (map text files))
+
+           Found _ _ -> panic "cantFindErr"
 \end{code}