033c50392124819da4418a71c1780002f5543c11
[ghc-hetmet.git] / ghc / compiler / main / Finder.lhs
1 %
2 % (c) The University of Glasgow, 2000
3 %
4 \section[Finder]{Module Finder}
5
6 \begin{code}
7 module Finder (
8     flushFinderCache,   -- :: IO ()
9     FindResult(..),
10     findModule,         -- :: ModuleName -> Bool -> IO FindResult
11     findPackageModule,  -- :: ModuleName -> Bool -> IO FindResult
12     mkHomeModLocation,  -- :: ModuleName -> FilePath -> IO ModLocation
13     findLinkable,       -- :: ModuleName -> ModLocation -> IO (Maybe Linkable)
14
15     hiBootExt,          -- :: String
16     hiBootVerExt,       -- :: String
17
18   ) where
19
20 #include "HsVersions.h"
21
22 import Module
23 import UniqFM           ( filterUFM )
24 import HscTypes         ( Linkable(..), Unlinked(..), IfacePackage(..) )
25 import Packages
26 import DriverState
27 import DriverUtil
28 import FastString
29 import Config
30 import Util
31 import CmdLineOpts      ( DynFlags(..) )
32
33 import DATA_IOREF       ( IORef, writeIORef, readIORef )
34
35 import Data.List
36 import System.Directory
37 import System.IO
38 import Control.Monad
39 import Data.Maybe       ( isNothing )
40
41 -- -----------------------------------------------------------------------------
42 -- The Finder
43
44 -- The Finder provides a thin filesystem abstraction to the rest of
45 -- the compiler.  For a given module, it can tell you where the
46 -- source, interface, and object files for that module live.
47 -- 
48 -- It does *not* know which particular package a module lives in.  Use
49 -- Packages.moduleToPackageConfig for that.
50
51 -- -----------------------------------------------------------------------------
52 -- The finder's cache
53
54 GLOBAL_VAR(finder_cache, emptyModuleEnv, ModuleEnv FinderCacheEntry)
55
56 type FinderCacheEntry = (ModLocation,Maybe (PackageConfig,Bool))
57
58 -- remove all the home modules from the cache; package modules are
59 -- assumed to not move around during a session.
60 flushFinderCache :: IO ()
61 flushFinderCache = do
62   fm <- readIORef finder_cache
63   writeIORef finder_cache (filterUFM (\(loc,m) -> isNothing m) fm)
64
65 addToFinderCache :: Module -> FinderCacheEntry -> IO ()
66 addToFinderCache mod_name entry = do
67   fm <- readIORef finder_cache
68   writeIORef finder_cache (extendModuleEnv fm mod_name entry)
69
70 lookupFinderCache :: Module -> IO (Maybe FinderCacheEntry)
71 lookupFinderCache mod_name = do
72   fm <- readIORef finder_cache
73   return $! lookupModuleEnv fm mod_name
74
75 -- -----------------------------------------------------------------------------
76 -- Locating modules
77
78 -- This is the main interface to the finder, which maps ModuleNames to
79 -- Modules and ModLocations.
80 --
81 -- The Module contains one crucial bit of information about a module:
82 -- whether it lives in the current ("home") package or not (see Module
83 -- for more details).
84 --
85 -- The ModLocation contains the names of all the files associated with
86 -- that module: its source file, .hi file, object file, etc.
87
88 data FindResult
89   = Found ModLocation IfacePackage
90         -- the module was found
91   | PackageHidden PackageId
92         -- for an explicit source import: the package containing the module is
93         -- not exposed.
94   | ModuleHidden  PackageId
95         -- for an explicit source import: the package containing the module is
96         -- exposed, but the module itself is hidden.
97   | NotFound [FilePath]
98         -- the module was not found, the specified places were searched.
99
100 findModule :: DynFlags -> Module -> Bool -> IO FindResult
101 findModule = cached findModule'
102   
103 findModule' :: DynFlags -> Module -> Bool -> IO FindResult
104 findModule' dflags name explicit = do
105     r <- findPackageModule' dflags name explicit
106     case r of
107         NotFound pkg_files -> do
108            j <- maybeHomeModule dflags name
109            case j of
110                 NotFound home_files -> 
111                         return (NotFound (home_files ++ pkg_files))
112                 other_result
113                         -> return other_result
114         other_result
115                 -> return other_result
116
117 cached fn dflags name explicit = do
118   m <- lookupFinderCache name
119   case m of
120     Nothing -> fn dflags name explicit
121     Just (loc,maybe_pkg)
122         | Just err <- visible explicit maybe_pkg  ->  return err
123         | otherwise -> return (Found loc (pkgInfoToId maybe_pkg))
124   
125 pkgInfoToId :: Maybe (PackageConfig,Bool) -> IfacePackage
126 pkgInfoToId (Just (pkg,_)) = ExternalPackage (mkPackageId (package pkg))
127 pkgInfoToId Nothing = ThisPackage
128
129 -- Is a module visible or not?  Returns Nothing if the import is ok,
130 -- or Just err if there's a visibility error.
131 visible :: Bool -> Maybe (PackageConfig,Bool) -> Maybe FindResult
132 visible explicit maybe_pkg
133    | Nothing <- maybe_pkg  =  Nothing   -- home module ==> YES
134    | not explicit          =  Nothing   -- implicit import ==> YES
135    | Just (pkg, exposed_module) <- maybe_pkg 
136     = case () of
137         _ | not exposed_module -> Just (ModuleHidden pkgname)
138           | not (exposed pkg)  -> Just (PackageHidden pkgname)
139           | otherwise          -> Nothing
140           where 
141                 pkgname = packageConfigId pkg
142      
143
144 hiBootExt = "hi-boot"
145 hiBootVerExt = "hi-boot-" ++ cHscIfaceFileVersion
146
147 maybeHomeModule :: DynFlags -> Module -> IO FindResult
148 maybeHomeModule dflags mod = do
149    let home_path = importPaths dflags
150    hisuf     <- readIORef v_Hi_suf
151    mode      <- readIORef v_GhcMode
152
153    let
154      source_exts = 
155       [ ("hs",   mkHomeModLocationSearched mod)
156       , ("lhs",  mkHomeModLocationSearched mod)
157       ]
158      
159      hi_exts = [ (hisuf,  mkHiOnlyModLocation hisuf mod) ]
160      
161      boot_exts =
162        [ (hiBootVerExt, mkHiOnlyModLocation hisuf mod)
163        , (hiBootExt,    mkHiOnlyModLocation hisuf mod)
164        ]
165
166         -- In compilation manager modes, we look for source files in the home
167         -- package because we can compile these automatically.  In one-shot
168         -- compilation mode we look for .hi and .hi-boot files only.
169         --
170         -- When generating dependencies, we're interested in either category.
171         --
172      exts
173          | mode == DoMkDependHS   = hi_exts ++ source_exts ++ boot_exts
174          | isCompManagerMode mode = source_exts
175          | otherwise {-one-shot-} = hi_exts ++ boot_exts
176
177    searchPathExts home_path mod exts
178         
179 -- -----------------------------------------------------------------------------
180 -- Looking for a package module
181
182 findPackageModule :: DynFlags -> Module -> Bool -> IO FindResult
183 findPackageModule = cached findPackageModule'
184
185 findPackageModule' :: DynFlags -> Module -> Bool -> IO FindResult
186 findPackageModule' dflags mod explicit = do
187   mode     <- readIORef v_GhcMode
188
189   case moduleToPackageConfig dflags mod of
190     Nothing -> return (NotFound [])
191     pkg_info@(Just (pkg_conf, module_exposed))
192         | Just err <- visible explicit pkg_info  ->  return err
193         | otherwise  ->  findPackageIface mode mod paths pkg_info
194       where 
195             paths   = importDirs pkg_conf
196
197 findPackageIface
198         :: GhcMode
199         -> Module
200         -> [FilePath]
201         -> Maybe (PackageConfig,Bool)
202         -> IO FindResult
203 findPackageIface mode mod imp_dirs pkg_info = do
204    -- hi-suffix for packages depends on the build tag.
205   package_hisuf <-
206         do tag <- readIORef v_Build_tag
207            if null tag
208                 then return "hi"
209                 else return (tag ++ "_hi")
210
211   let
212      hi_exts =
213         [ (package_hisuf, 
214             mkPackageModLocation pkg_info package_hisuf mod) ]
215
216      source_exts = 
217        [ ("hs",   mkPackageModLocation pkg_info package_hisuf mod)
218        , ("lhs",  mkPackageModLocation pkg_info package_hisuf mod)
219        ]
220
221      -- mkdependHS needs to look for source files in packages too, so
222      -- that we can make dependencies between package before they have
223      -- been built.
224      exts 
225       | mode == DoMkDependHS = hi_exts ++ source_exts
226       | otherwise = hi_exts
227
228       -- we never look for a .hi-boot file in an external package;
229       -- .hi-boot files only make sense for the home package.
230   searchPathExts imp_dirs mod exts
231
232 -- -----------------------------------------------------------------------------
233 -- General path searching
234
235 searchPathExts
236   :: [FilePath]         -- paths to search
237   -> Module             -- module name
238   -> [ (
239         String,                                      -- suffix
240         String -> String -> String -> IO FindResult  -- action
241        )
242      ] 
243   -> IO FindResult
244
245 searchPathExts path mod exts = search to_search
246   where
247     basename = dots_to_slashes (moduleUserString mod)
248
249     to_search :: [(FilePath, IO FindResult)]
250     to_search = [ (file, fn p basename ext)
251                 | p <- path, 
252                   (ext,fn) <- exts,
253                   let base | p == "."  = basename
254                            | otherwise = p ++ '/':basename
255                       file = base ++ '.':ext
256                 ]
257
258     search [] = return (NotFound (map fst to_search))
259     search ((file, result) : rest) = do
260       b <- doesFileExist file
261       if b 
262         then result
263         else search rest
264
265 -- -----------------------------------------------------------------------------
266 -- Building ModLocations
267
268 mkHiOnlyModLocation hisuf mod path basename _ext = do
269   -- basename == dots_to_slashes (moduleNameUserString mod)
270   loc <- hiOnlyModLocation path basename hisuf
271   addToFinderCache mod (loc, Nothing)
272   return (Found loc ThisPackage)
273
274 mkPackageModLocation pkg_info hisuf mod path basename _ext = do
275   -- basename == dots_to_slashes (moduleNameUserString mod)
276   loc <- hiOnlyModLocation path basename hisuf
277   addToFinderCache mod (loc, pkg_info)
278   return (Found loc (pkgInfoToId pkg_info))
279
280 hiOnlyModLocation path basename hisuf 
281  = do let full_basename = path++'/':basename
282       obj_fn <- mkObjPath full_basename basename
283       return ModLocation{ ml_hspp_file = Nothing,
284                              ml_hs_file   = Nothing,
285                              ml_hi_file   = full_basename ++ '.':hisuf,
286                                 -- Remove the .hi-boot suffix from
287                                 -- hi_file, if it had one.  We always
288                                 -- want the name of the real .hi file
289                                 -- in the ml_hi_file field.
290                              ml_obj_file  = obj_fn
291                   }
292
293 -- -----------------------------------------------------------------------------
294 -- Constructing a home module location
295
296 -- This is where we construct the ModLocation for a module in the home
297 -- package, for which we have a source file.  It is called from three
298 -- places:
299 --
300 --  (a) Here in the finder, when we are searching for a module to import,
301 --      using the search path (-i option).
302 --
303 --  (b) The compilation manager, when constructing the ModLocation for
304 --      a "root" module (a source file named explicitly on the command line
305 --      or in a :load command in GHCi).
306 --
307 --  (c) The driver in one-shot mode, when we need to construct a
308 --      ModLocation for a source file named on the command-line.
309 --
310 -- Parameters are:
311 --
312 -- mod
313 --      The name of the module
314 --
315 -- path
316 --      (a): The search path component where the source file was found.
317 --      (b) and (c): "."
318 --
319 -- src_basename
320 --      (a): dots_to_slashes (moduleNameUserString mod)
321 --      (b) and (c): The filename of the source file, minus its extension
322 --
323 -- ext
324 --      The filename extension of the source file (usually "hs" or "lhs").
325
326 mkHomeModLocation mod src_filename = do
327    let (basename,extension) = splitFilename src_filename
328    mkHomeModLocation' mod basename extension
329
330 mkHomeModLocationSearched mod path basename ext = do
331    loc <- mkHomeModLocation' mod (path ++ '/':basename) ext
332    return (Found loc ThisPackage)
333
334 mkHomeModLocation' mod src_basename ext = do
335    let mod_basename = dots_to_slashes (moduleUserString mod)
336
337    obj_fn <- mkObjPath src_basename mod_basename
338    hi_fn  <- mkHiPath  src_basename mod_basename
339
340    let loc = ModLocation{ ml_hspp_file = Nothing,
341                           ml_hs_file   = Just (src_basename ++ '.':ext),
342                           ml_hi_file   = hi_fn,
343                           ml_obj_file  = obj_fn }
344
345    addToFinderCache mod (loc, Nothing)
346    return loc
347
348 -- | Constructs the filename of a .o file for a given source file.
349 -- Does /not/ check whether the .o file exists
350 mkObjPath
351   :: FilePath           -- the filename of the source file, minus the extension
352   -> String             -- the module name with dots replaced by slashes
353   -> IO FilePath
354 mkObjPath basename mod_basename
355   = do  odir   <- readIORef v_Output_dir
356         osuf   <- readIORef v_Object_suf
357
358         let obj_basename | Just dir <- odir = dir ++ '/':mod_basename
359                          | otherwise        = basename
360
361         return (obj_basename ++ '.':osuf)
362
363 -- | Constructs the filename of a .hi file for a given source file.
364 -- Does /not/ check whether the .hi file exists
365 mkHiPath
366   :: FilePath           -- the filename of the source file, minus the extension
367   -> String             -- the module name with dots replaced by slashes
368   -> IO FilePath
369 mkHiPath basename mod_basename
370   = do  hidir   <- readIORef v_Hi_dir
371         hisuf   <- readIORef v_Hi_suf
372
373         let hi_basename | Just dir <- hidir = dir ++ '/':mod_basename
374                         | otherwise         = basename
375
376         return (hi_basename ++ '.':hisuf)
377
378 -- -----------------------------------------------------------------------------
379 -- findLinkable isn't related to the other stuff in here, 
380 -- but there's no other obvious place for it
381
382 findLinkable :: Module -> ModLocation -> IO (Maybe Linkable)
383 findLinkable mod locn
384    = do let obj_fn = ml_obj_file locn
385         obj_exist <- doesFileExist obj_fn
386         if not obj_exist 
387          then return Nothing 
388          else 
389          do let stub_fn = case splitFilename3 obj_fn of
390                              (dir, base, ext) -> dir ++ "/" ++ base ++ "_stub.o"
391             stub_exist <- doesFileExist stub_fn
392             obj_time <- getModificationTime obj_fn
393             if stub_exist
394              then return (Just (LM obj_time mod [DotO obj_fn, DotO stub_fn]))
395              else return (Just (LM obj_time mod [DotO obj_fn]))
396
397 -- -----------------------------------------------------------------------------
398 -- Utils
399
400 dots_to_slashes = map (\c -> if c == '.' then '/' else c)
401
402 \end{code}