[project @ 2004-11-26 16:19:45 by simonmar]
[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    j <- maybeHomeModule dflags name
106    case j of
107         NotFound home_files -> do
108             r <- findPackageModule' dflags name explicit
109             case r of
110                 NotFound pkg_files 
111                         -> return (NotFound (home_files ++ pkg_files))
112                 other_result
113                         -> return other_result
114         other_result -> return other_result
115
116 cached fn dflags name explicit = do
117   m <- lookupFinderCache name
118   case m of
119     Nothing -> fn dflags name explicit
120     Just (loc,maybe_pkg)
121         | Just err <- visible explicit maybe_pkg  ->  return err
122         | otherwise -> return (Found loc (pkgInfoToId maybe_pkg))
123   
124 pkgInfoToId :: Maybe (PackageConfig,Bool) -> IfacePackage
125 pkgInfoToId (Just (pkg,_)) = ExternalPackage (mkPackageId (package pkg))
126 pkgInfoToId Nothing = ThisPackage
127
128 -- Is a module visible or not?  Returns Nothing if the import is ok,
129 -- or Just err if there's a visibility error.
130 visible :: Bool -> Maybe (PackageConfig,Bool) -> Maybe FindResult
131 visible explicit maybe_pkg
132    | Nothing <- maybe_pkg  =  Nothing   -- home module ==> YES
133    | not explicit          =  Nothing   -- implicit import ==> YES
134    | Just (pkg, exposed_module) <- maybe_pkg 
135     = case () of
136         _ | not exposed_module -> Just (ModuleHidden pkgname)
137           | not (exposed pkg)  -> Just (PackageHidden pkgname)
138           | otherwise          -> Nothing
139           where 
140                 pkgname = packageConfigId pkg
141      
142
143 hiBootExt = "hi-boot"
144 hiBootVerExt = "hi-boot-" ++ cHscIfaceFileVersion
145
146 maybeHomeModule :: DynFlags -> Module -> IO FindResult
147 maybeHomeModule dflags mod = do
148    let home_path = importPaths dflags
149    hisuf     <- readIORef v_Hi_suf
150    mode      <- readIORef v_GhcMode
151
152    let
153      source_exts = 
154       [ ("hs",   mkHomeModLocationSearched mod)
155       , ("lhs",  mkHomeModLocationSearched mod)
156       ]
157      
158      hi_exts = [ (hisuf,  mkHiOnlyModLocation hisuf mod) ]
159      
160      boot_exts =
161        [ (hiBootVerExt, mkHiOnlyModLocation hisuf mod)
162        , (hiBootExt,    mkHiOnlyModLocation hisuf mod)
163        ]
164
165         -- In compilation manager modes, we look for source files in the home
166         -- package because we can compile these automatically.  In one-shot
167         -- compilation mode we look for .hi and .hi-boot files only.
168         --
169         -- When generating dependencies, we're interested in either category.
170         --
171      exts
172          | mode == DoMkDependHS   = hi_exts ++ source_exts ++ boot_exts
173          | isCompManagerMode mode = source_exts
174          | otherwise {-one-shot-} = hi_exts ++ boot_exts
175
176    searchPathExts home_path mod exts
177         
178 -- -----------------------------------------------------------------------------
179 -- Looking for a package module
180
181 findPackageModule :: DynFlags -> Module -> Bool -> IO FindResult
182 findPackageModule = cached findPackageModule'
183
184 findPackageModule' :: DynFlags -> Module -> Bool -> IO FindResult
185 findPackageModule' dflags mod explicit = do
186   mode     <- readIORef v_GhcMode
187
188   case moduleToPackageConfig dflags mod of
189     Nothing -> return (NotFound [])
190     pkg_info@(Just (pkg_conf, module_exposed))
191         | Just err <- visible explicit pkg_info  ->  return err
192         | otherwise  ->  findPackageIface mode mod paths pkg_info
193       where 
194             paths   = importDirs pkg_conf
195
196 findPackageIface
197         :: GhcMode
198         -> Module
199         -> [FilePath]
200         -> Maybe (PackageConfig,Bool)
201         -> IO FindResult
202 findPackageIface mode mod imp_dirs pkg_info = do
203    -- hi-suffix for packages depends on the build tag.
204   package_hisuf <-
205         do tag <- readIORef v_Build_tag
206            if null tag
207                 then return "hi"
208                 else return (tag ++ "_hi")
209
210   let
211      hi_exts =
212         [ (package_hisuf, 
213             mkPackageModLocation pkg_info package_hisuf mod) ]
214
215      source_exts = 
216        [ ("hs",   mkPackageModLocation pkg_info package_hisuf mod)
217        , ("lhs",  mkPackageModLocation pkg_info package_hisuf mod)
218        ]
219
220      -- mkdependHS needs to look for source files in packages too, so
221      -- that we can make dependencies between package before they have
222      -- been built.
223      exts 
224       | mode == DoMkDependHS = hi_exts ++ source_exts
225       | otherwise = hi_exts
226
227       -- we never look for a .hi-boot file in an external package;
228       -- .hi-boot files only make sense for the home package.
229   searchPathExts imp_dirs mod exts
230
231 -- -----------------------------------------------------------------------------
232 -- General path searching
233
234 searchPathExts
235   :: [FilePath]         -- paths to search
236   -> Module             -- module name
237   -> [ (
238         String,                                      -- suffix
239         String -> String -> String -> IO FindResult  -- action
240        )
241      ] 
242   -> IO FindResult
243
244 searchPathExts path mod exts = search to_search
245   where
246     basename = dots_to_slashes (moduleUserString mod)
247
248     to_search :: [(FilePath, IO FindResult)]
249     to_search = [ (file, fn p basename ext)
250                 | p <- path, 
251                   (ext,fn) <- exts,
252                   let base | p == "."  = basename
253                            | otherwise = p ++ '/':basename
254                       file = base ++ '.':ext
255                 ]
256
257     search [] = return (NotFound (map fst to_search))
258     search ((file, result) : rest) = do
259       b <- doesFileExist file
260       if b 
261         then result
262         else search rest
263
264 -- -----------------------------------------------------------------------------
265 -- Building ModLocations
266
267 mkHiOnlyModLocation hisuf mod path basename _ext = do
268   -- basename == dots_to_slashes (moduleNameUserString mod)
269   loc <- hiOnlyModLocation path basename hisuf
270   addToFinderCache mod (loc, Nothing)
271   return (Found loc ThisPackage)
272
273 mkPackageModLocation pkg_info hisuf mod path basename _ext = do
274   -- basename == dots_to_slashes (moduleNameUserString mod)
275   loc <- hiOnlyModLocation path basename hisuf
276   addToFinderCache mod (loc, pkg_info)
277   return (Found loc (pkgInfoToId pkg_info))
278
279 hiOnlyModLocation path basename hisuf 
280  = do let full_basename = path++'/':basename
281       obj_fn <- mkObjPath full_basename basename
282       return ModLocation{ ml_hspp_file = Nothing,
283                              ml_hs_file   = Nothing,
284                              ml_hi_file   = full_basename ++ '.':hisuf,
285                                 -- Remove the .hi-boot suffix from
286                                 -- hi_file, if it had one.  We always
287                                 -- want the name of the real .hi file
288                                 -- in the ml_hi_file field.
289                              ml_obj_file  = obj_fn
290                   }
291
292 -- -----------------------------------------------------------------------------
293 -- Constructing a home module location
294
295 -- This is where we construct the ModLocation for a module in the home
296 -- package, for which we have a source file.  It is called from three
297 -- places:
298 --
299 --  (a) Here in the finder, when we are searching for a module to import,
300 --      using the search path (-i option).
301 --
302 --  (b) The compilation manager, when constructing the ModLocation for
303 --      a "root" module (a source file named explicitly on the command line
304 --      or in a :load command in GHCi).
305 --
306 --  (c) The driver in one-shot mode, when we need to construct a
307 --      ModLocation for a source file named on the command-line.
308 --
309 -- Parameters are:
310 --
311 -- mod
312 --      The name of the module
313 --
314 -- path
315 --      (a): The search path component where the source file was found.
316 --      (b) and (c): "."
317 --
318 -- src_basename
319 --      (a): dots_to_slashes (moduleNameUserString mod)
320 --      (b) and (c): The filename of the source file, minus its extension
321 --
322 -- ext
323 --      The filename extension of the source file (usually "hs" or "lhs").
324
325 mkHomeModLocation mod src_filename = do
326    let (basename,extension) = splitFilename src_filename
327    mkHomeModLocation' mod basename extension
328
329 mkHomeModLocationSearched mod path basename ext = do
330    loc <- mkHomeModLocation' mod (path ++ '/':basename) ext
331    return (Found loc ThisPackage)
332
333 mkHomeModLocation' mod src_basename ext = do
334    let mod_basename = dots_to_slashes (moduleUserString mod)
335
336    obj_fn <- mkObjPath src_basename mod_basename
337    hi_fn  <- mkHiPath  src_basename mod_basename
338
339    let loc = ModLocation{ ml_hspp_file = Nothing,
340                           ml_hs_file   = Just (src_basename ++ '.':ext),
341                           ml_hi_file   = hi_fn,
342                           ml_obj_file  = obj_fn }
343
344    addToFinderCache mod (loc, Nothing)
345    return loc
346
347 -- | Constructs the filename of a .o file for a given source file.
348 -- Does /not/ check whether the .o file exists
349 mkObjPath
350   :: FilePath           -- the filename of the source file, minus the extension
351   -> String             -- the module name with dots replaced by slashes
352   -> IO FilePath
353 mkObjPath basename mod_basename
354   = do  odir   <- readIORef v_Output_dir
355         osuf   <- readIORef v_Object_suf
356
357         let obj_basename | Just dir <- odir = dir ++ '/':mod_basename
358                          | otherwise        = basename
359
360         return (obj_basename ++ '.':osuf)
361
362 -- | Constructs the filename of a .hi file for a given source file.
363 -- Does /not/ check whether the .hi file exists
364 mkHiPath
365   :: FilePath           -- the filename of the source file, minus the extension
366   -> String             -- the module name with dots replaced by slashes
367   -> IO FilePath
368 mkHiPath basename mod_basename
369   = do  hidir   <- readIORef v_Hi_dir
370         hisuf   <- readIORef v_Hi_suf
371
372         let hi_basename | Just dir <- hidir = dir ++ '/':mod_basename
373                         | otherwise         = basename
374
375         return (hi_basename ++ '.':hisuf)
376
377 -- -----------------------------------------------------------------------------
378 -- findLinkable isn't related to the other stuff in here, 
379 -- but there's no other obvious place for it
380
381 findLinkable :: Module -> ModLocation -> IO (Maybe Linkable)
382 findLinkable mod locn
383    = do let obj_fn = ml_obj_file locn
384         obj_exist <- doesFileExist obj_fn
385         if not obj_exist 
386          then return Nothing 
387          else 
388          do let stub_fn = case splitFilename3 obj_fn of
389                              (dir, base, ext) -> dir ++ "/" ++ base ++ "_stub.o"
390             stub_exist <- doesFileExist stub_fn
391             obj_time <- getModificationTime obj_fn
392             if stub_exist
393              then return (Just (LM obj_time mod [DotO obj_fn, DotO stub_fn]))
394              else return (Just (LM obj_time mod [DotO obj_fn]))
395
396 -- -----------------------------------------------------------------------------
397 -- Utils
398
399 dots_to_slashes = map (\c -> if c == '.' then '/' else c)
400
401 \end{code}