[project @ 2005-03-24 16:14:00 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     mkHomeModLocation2,         -- :: ModuleName -> FilePath -> String -> IO ModLocation
14     addHomeModuleToFinder,      -- :: HscEnv -> Module -> ModLocation -> IO ()
15
16     findObjectLinkableMaybe,
17     findObjectLinkable,
18
19     cantFindError,      -- :: DynFlags -> Module -> FindResult -> SDoc
20   ) where
21
22 #include "HsVersions.h"
23
24 import Module
25 import UniqFM           ( filterUFM )
26 import HscTypes
27 import Packages
28 import FastString
29 import Util
30 import DynFlags         ( DynFlags(..), isOneShot, GhcMode(..) )
31 import Outputable
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 Maybes           ( MaybeErr(..) )
40 import Data.Maybe       ( isNothing )
41 import Time             ( ClockTime )
42
43
44 type FileExt = String   -- Filename extension
45 type BaseName = String  -- Basename of file
46
47 -- -----------------------------------------------------------------------------
48 -- The Finder
49
50 -- The Finder provides a thin filesystem abstraction to the rest of
51 -- the compiler.  For a given module, it can tell you where the
52 -- source, interface, and object files for that module live.
53
54 -- It does *not* know which particular package a module lives in.  Use
55 -- Packages.moduleToPackageConfig for that.
56
57 -- -----------------------------------------------------------------------------
58 -- The finder's cache
59
60 -- remove all the home modules from the cache; package modules are
61 -- assumed to not move around during a session.
62 flushFinderCache :: IORef FinderCache -> IO ()
63 flushFinderCache finder_cache = do
64   fm <- readIORef finder_cache
65   writeIORef finder_cache $! filterUFM (\(loc,m) -> isNothing m) fm
66
67 addToFinderCache :: IORef FinderCache -> Module -> FinderCacheEntry -> IO ()
68 addToFinderCache finder_cache mod_name entry = do
69   fm <- readIORef finder_cache
70   writeIORef finder_cache $! extendModuleEnv fm mod_name entry
71
72 lookupFinderCache :: IORef FinderCache -> Module -> IO (Maybe FinderCacheEntry)
73 lookupFinderCache finder_cache mod_name = do
74   fm <- readIORef finder_cache
75   return $! lookupModuleEnv fm mod_name
76
77 -- -----------------------------------------------------------------------------
78 -- Locating modules
79
80 -- This is the main interface to the finder, which maps ModuleNames to
81 -- Modules and ModLocations.
82 --
83 -- The Module contains one crucial bit of information about a module:
84 -- whether it lives in the current ("home") package or not (see Module
85 -- for more details).
86 --
87 -- The ModLocation contains the names of all the files associated with
88 -- that module: its source file, .hi file, object file, etc.
89
90 data FindResult
91   = Found ModLocation PackageIdH
92         -- the module was found
93   | PackageHidden PackageId
94         -- for an explicit source import: the package containing the module is
95         -- not exposed.
96   | ModuleHidden  PackageId
97         -- for an explicit source import: the package containing the module is
98         -- exposed, but the module itself is hidden.
99   | NotFound [FilePath]
100         -- the module was not found, the specified places were searched.
101
102 type LocalFindResult = MaybeErr [FilePath] FinderCacheEntry
103         -- LocalFindResult is used for internal functions which 
104         -- return a more informative type; it's munged into
105         -- the external FindResult by 'cached'
106
107 cached :: Bool
108        -> (DynFlags -> Module -> IO LocalFindResult)
109        -> HscEnv -> Module -> Bool -> IO FindResult
110 cached home_allowed wrapped_fn hsc_env name explicit 
111   = do  {       -- First try the cache
112           let cache = hsc_FC hsc_env
113         ; mb_entry <- lookupFinderCache cache name
114         ; case mb_entry of {
115             Just old_entry -> return (found old_entry) ;
116             Nothing    -> do
117
118         {       -- Now try the wrapped function
119           mb_entry <- wrapped_fn (hsc_dflags hsc_env) name
120         ; case mb_entry of
121             Failed paths        -> return (NotFound paths)
122             Succeeded new_entry -> do { addToFinderCache cache name new_entry
123                                       ; return (found new_entry) }
124         }}} 
125   where
126         -- We've found the module, so the remaining question is
127         -- whether it's visible or not
128     found :: FinderCacheEntry -> FindResult
129     found (loc, Nothing)
130         | home_allowed  = Found loc HomePackage
131         | otherwise     = NotFound []
132     found (loc, Just (pkg, exposed_mod))
133         | explicit && not exposed_mod   = ModuleHidden pkg_name
134         | explicit && not (exposed pkg) = PackageHidden pkg_name
135         | otherwise                     = Found loc (ExtPackage (mkPackageId (package pkg)))
136         where
137           pkg_name = packageConfigId pkg
138
139 addHomeModuleToFinder :: HscEnv -> Module -> ModLocation -> IO ()
140 addHomeModuleToFinder hsc_env mod loc 
141   = addToFinderCache (hsc_FC hsc_env) mod (loc, Nothing)
142
143
144 -- -----------------------------------------------------------------------------
145 --      The two external entry points
146
147
148 findModule :: HscEnv -> Module -> Bool -> IO FindResult
149 findModule = cached True findModule' 
150   
151 findPackageModule :: HscEnv -> Module -> Bool -> IO FindResult
152 findPackageModule = cached False findPackageModule'
153
154 -- -----------------------------------------------------------------------------
155 --      The internal workers
156
157 findModule' :: DynFlags -> Module -> IO LocalFindResult
158 -- Find home or package module
159 findModule' dflags name = do
160     r <- findPackageModule' dflags name
161     case r of
162         Failed pkg_files -> do
163            j <- findHomeModule' dflags name
164            case j of
165                 Failed home_files -> 
166                         return (Failed (home_files ++ pkg_files))
167                 other_result
168                         -> return other_result
169         other_result
170                 -> return other_result
171
172 findHomeModule' :: DynFlags -> Module -> IO LocalFindResult
173 findHomeModule' dflags mod = do
174    let home_path = importPaths dflags
175        hisuf = hiSuf dflags
176
177    let
178      source_exts = 
179       [ ("hs",   mkHomeModLocationSearched dflags mod "hs")
180       , ("lhs",  mkHomeModLocationSearched dflags  mod "lhs")
181       ]
182      
183      hi_exts = [ (hisuf,                mkHiOnlyModLocation dflags hisuf)
184                , (addBootSuffix hisuf,  mkHiOnlyModLocation dflags hisuf)
185                ]
186      
187         -- In compilation manager modes, we look for source files in the home
188         -- package because we can compile these automatically.  In one-shot
189         -- compilation mode we look for .hi and .hi-boot files only.
190      exts | isOneShot (ghcMode dflags) = hi_exts
191           | otherwise                  = source_exts
192
193    searchPathExts home_path mod exts
194         
195 findPackageModule' :: DynFlags -> Module -> IO LocalFindResult
196 findPackageModule' dflags mod 
197   = case moduleToPackageConfig dflags mod of
198         Nothing       -> return (Failed [])
199         Just pkg_info -> findPackageIface dflags mod pkg_info
200
201 findPackageIface :: DynFlags -> Module -> (PackageConfig,Bool) -> IO LocalFindResult
202 findPackageIface dflags mod pkg_info@(pkg_conf, _) = do
203   let
204      tag = buildTag dflags
205
206            -- hi-suffix for packages depends on the build tag.
207      package_hisuf | null tag  = "hi"
208                    | otherwise = tag ++ "_hi"
209      hi_exts =
210         [ (package_hisuf, 
211             mkPackageModLocation dflags pkg_info package_hisuf) ]
212
213      source_exts = 
214        [ ("hs",   mkPackageModLocation dflags pkg_info package_hisuf)
215        , ("lhs",  mkPackageModLocation dflags pkg_info package_hisuf)
216        ]
217
218      -- mkdependHS needs to look for source files in packages too, so
219      -- that we can make dependencies between package before they have
220      -- been built.
221      exts 
222       | MkDepend <- ghcMode dflags = hi_exts ++ source_exts
223       | otherwise                  = hi_exts
224       -- we never look for a .hi-boot file in an external package;
225       -- .hi-boot files only make sense for the home package.
226
227   searchPathExts (importDirs pkg_conf) mod exts
228
229 -- -----------------------------------------------------------------------------
230 -- General path searching
231
232 searchPathExts
233   :: [FilePath]         -- paths to search
234   -> Module             -- module name
235   -> [ (
236         FileExt,                                     -- suffix
237         FilePath -> BaseName -> IO FinderCacheEntry  -- action
238        )
239      ] 
240   -> IO LocalFindResult
241
242 searchPathExts paths mod exts 
243    = do result <- search to_search
244 {-
245         hPutStrLn stderr (showSDoc $
246                 vcat [text "Search" <+> ppr mod <+> sep (map (text. fst) exts)
247                     , nest 2 (vcat (map text paths))
248                     , case result of
249                         Succeeded (loc, p) -> text "Found" <+> ppr loc
250                         Failed fs          -> text "not found"])
251 -}      
252         return result
253
254   where
255     basename = dots_to_slashes (moduleUserString mod)
256
257     to_search :: [(FilePath, IO FinderCacheEntry)]
258     to_search = [ (file, fn path basename)
259                 | path <- paths, 
260                   (ext,fn) <- exts,
261                   let base | path == "." = basename
262                            | otherwise   = path ++ '/':basename
263                       file = base ++ '.':ext
264                 ]
265
266     search [] = return (Failed (map fst to_search))
267     search ((file, mk_result) : rest) = do
268       b <- doesFileExist file
269       if b 
270         then do { res <- mk_result; return (Succeeded res) }
271         else search rest
272
273 mkHomeModLocationSearched :: DynFlags -> Module -> FileExt
274                           -> FilePath -> BaseName -> IO FinderCacheEntry
275 mkHomeModLocationSearched dflags mod suff path basename = do
276    loc <- mkHomeModLocation2 dflags mod (path ++ '/':basename) suff
277    return (loc, Nothing)
278
279 mkHiOnlyModLocation :: DynFlags -> FileExt -> FilePath -> BaseName
280                     -> IO FinderCacheEntry
281 mkHiOnlyModLocation dflags hisuf path basename = do
282   loc <- hiOnlyModLocation dflags path basename hisuf
283   return (loc, Nothing)
284
285 mkPackageModLocation :: DynFlags -> (PackageConfig, Bool) -> FileExt
286                      -> FilePath -> BaseName -> IO FinderCacheEntry
287 mkPackageModLocation dflags pkg_info hisuf path basename = do
288   loc <- hiOnlyModLocation dflags path basename hisuf
289   return (loc, Just pkg_info)
290
291 -- -----------------------------------------------------------------------------
292 -- Constructing a home module location
293
294 -- This is where we construct the ModLocation for a module in the home
295 -- package, for which we have a source file.  It is called from three
296 -- places:
297 --
298 --  (a) Here in the finder, when we are searching for a module to import,
299 --      using the search path (-i option).
300 --
301 --  (b) The compilation manager, when constructing the ModLocation for
302 --      a "root" module (a source file named explicitly on the command line
303 --      or in a :load command in GHCi).
304 --
305 --  (c) The driver in one-shot mode, when we need to construct a
306 --      ModLocation for a source file named on the command-line.
307 --
308 -- Parameters are:
309 --
310 -- mod
311 --      The name of the module
312 --
313 -- path
314 --      (a): The search path component where the source file was found.
315 --      (b) and (c): "."
316 --
317 -- src_basename
318 --      (a): dots_to_slashes (moduleNameUserString mod)
319 --      (b) and (c): The filename of the source file, minus its extension
320 --
321 -- ext
322 --      The filename extension of the source file (usually "hs" or "lhs").
323
324 mkHomeModLocation :: DynFlags -> Module -> FilePath -> IO ModLocation
325 mkHomeModLocation dflags mod src_filename = do
326    let (basename,extension) = splitFilename src_filename
327    mkHomeModLocation2 dflags mod basename extension
328
329 mkHomeModLocation2 :: DynFlags
330                    -> Module    
331                    -> FilePath  -- Of source module, without suffix
332                    -> String    -- Suffix
333                    -> IO ModLocation
334 mkHomeModLocation2 dflags mod src_basename ext = do
335    let mod_basename = dots_to_slashes (moduleUserString mod)
336
337    obj_fn <- mkObjPath dflags src_basename mod_basename
338    hi_fn  <- mkHiPath  dflags src_basename mod_basename
339
340    return (ModLocation{ ml_hs_file   = Just (src_basename ++ '.':ext),
341                         ml_hi_file   = hi_fn,
342                         ml_obj_file  = obj_fn })
343
344 hiOnlyModLocation :: DynFlags -> FilePath -> String -> Suffix -> IO ModLocation
345 hiOnlyModLocation dflags path basename hisuf 
346  = do let full_basename = path++'/':basename
347       obj_fn <- mkObjPath dflags full_basename basename
348       return ModLocation{    ml_hs_file   = Nothing,
349                              ml_hi_file   = full_basename ++ '.':hisuf,
350                                 -- Remove the .hi-boot suffix from
351                                 -- hi_file, if it had one.  We always
352                                 -- want the name of the real .hi file
353                                 -- in the ml_hi_file field.
354                              ml_obj_file  = obj_fn
355                   }
356
357 -- | Constructs the filename of a .o file for a given source file.
358 -- Does /not/ check whether the .o file exists
359 mkObjPath
360   :: DynFlags
361   -> FilePath           -- the filename of the source file, minus the extension
362   -> String             -- the module name with dots replaced by slashes
363   -> IO FilePath
364 mkObjPath dflags basename mod_basename
365   = do  let
366                 odir = outputDir dflags
367                 osuf = objectSuf dflags
368         
369                 obj_basename | Just dir <- odir = dir ++ '/':mod_basename
370                              | otherwise        = basename
371
372         return (obj_basename ++ '.':osuf)
373
374 -- | Constructs the filename of a .hi file for a given source file.
375 -- Does /not/ check whether the .hi file exists
376 mkHiPath
377   :: DynFlags
378   -> FilePath           -- the filename of the source file, minus the extension
379   -> String             -- the module name with dots replaced by slashes
380   -> IO FilePath
381 mkHiPath dflags basename mod_basename
382   = do  let
383                 hidir = hiDir dflags
384                 hisuf = hiSuf dflags
385
386                 hi_basename | Just dir <- hidir = dir ++ '/':mod_basename
387                             | otherwise         = basename
388
389         return (hi_basename ++ '.':hisuf)
390
391
392 -- -----------------------------------------------------------------------------
393 -- findLinkable isn't related to the other stuff in here, 
394 -- but there's no other obvious place for it
395
396 findObjectLinkableMaybe :: Module -> ModLocation -> IO (Maybe Linkable)
397 findObjectLinkableMaybe mod locn
398    = do let obj_fn = ml_obj_file locn
399         maybe_obj_time <- modificationTimeIfExists obj_fn
400         case maybe_obj_time of
401           Nothing -> return Nothing
402           Just obj_time -> liftM Just (findObjectLinkable mod obj_fn obj_time)
403
404 -- Make an object linkable when we know the object file exists, and we know
405 -- its modification time.
406 findObjectLinkable :: Module -> FilePath -> ClockTime -> IO Linkable
407 findObjectLinkable mod obj_fn obj_time = do
408   let stub_fn = case splitFilename3 obj_fn of
409                         (dir, base, ext) -> dir ++ "/" ++ base ++ "_stub.o"
410   stub_exist <- doesFileExist stub_fn
411   if stub_exist
412         then return (LM obj_time mod [DotO obj_fn, DotO stub_fn])
413         else return (LM obj_time mod [DotO obj_fn])
414
415 -- -----------------------------------------------------------------------------
416 -- Utils
417
418 dots_to_slashes = map (\c -> if c == '.' then '/' else c)
419
420
421 -- -----------------------------------------------------------------------------
422 -- Error messages
423
424 cantFindError :: DynFlags -> Module -> FindResult -> SDoc
425 cantFindError dflags mod_name find_result
426   = hang (ptext SLIT("Could not find module") <+> quotes (ppr mod_name) <> colon)
427        2 more_info
428   where
429     more_info
430       = case find_result of
431             PackageHidden pkg 
432                 -> ptext SLIT("it is a member of package") <+> ppr pkg <> comma
433                    <+> ptext SLIT("which is hidden")
434
435             ModuleHidden pkg
436                 -> ptext SLIT("it is hidden") <+> parens (ptext SLIT("in package")
437                    <+> ppr pkg)
438
439             NotFound files
440                 | null files
441                 -> ptext SLIT("it is not a module in the current program, or in any known package.")
442                 | verbosity dflags < 3 
443                 -> ptext SLIT("use -v to see a list of the files searched for")
444                 | otherwise 
445                 -> hang (ptext SLIT("locations searched:")) 
446                       2 (vcat (map text files))
447
448             Found _ _ -> panic "cantFindErr"
449 \end{code}