[project @ 2005-03-18 13:37:27 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,      -- :: Module -> ModLocation -> IO ()
15
16     findLinkable,       -- :: ModuleName -> ModLocation -> IO (Maybe Linkable)
17
18     cantFindError,      -- :: DynFlags -> Module -> FindResult -> SDoc
19   ) where
20
21 #include "HsVersions.h"
22
23 import Module
24 import UniqFM           ( filterUFM )
25 import HscTypes         ( Linkable(..), Unlinked(..) )
26 import Packages
27 import FastString
28 import Util
29 import DynFlags         ( DynFlags(..), isOneShot, GhcMode(..) )
30 import Outputable
31
32 import DATA_IOREF       ( IORef, writeIORef, readIORef )
33
34 import Data.List
35 import System.Directory
36 import System.IO
37 import Control.Monad
38 import Maybes           ( MaybeErr(..) )
39 import Data.Maybe       ( isNothing )
40
41
42 type FileExt = String   -- Filename extension
43 type BaseName = String  -- Basename of file
44
45 -- -----------------------------------------------------------------------------
46 -- The Finder
47
48 -- The Finder provides a thin filesystem abstraction to the rest of
49 -- the compiler.  For a given module, it can tell you where the
50 -- source, interface, and object files for that module live.
51
52 -- It does *not* know which particular package a module lives in.  Use
53 -- Packages.moduleToPackageConfig for that.
54
55 -- -----------------------------------------------------------------------------
56 -- The finder's cache
57
58 GLOBAL_VAR(finder_cache, emptyModuleEnv, ModuleEnv FinderCacheEntry)
59
60 type FinderCacheEntry = (ModLocation, Maybe (PackageConfig,Bool))
61
62 -- remove all the home modules from the cache; package modules are
63 -- assumed to not move around during a session.
64 flushFinderCache :: IO ()
65 flushFinderCache = do
66   fm <- readIORef finder_cache
67   writeIORef finder_cache (filterUFM (\(loc,m) -> isNothing m) fm)
68
69 addToFinderCache :: Module -> FinderCacheEntry -> IO ()
70 addToFinderCache mod_name entry = do
71   fm <- readIORef finder_cache
72   writeIORef finder_cache (extendModuleEnv fm mod_name entry)
73
74 lookupFinderCache :: Module -> IO (Maybe FinderCacheEntry)
75 lookupFinderCache mod_name = do
76   fm <- readIORef finder_cache
77   return $! lookupModuleEnv fm mod_name
78
79 -- -----------------------------------------------------------------------------
80 -- Locating modules
81
82 -- This is the main interface to the finder, which maps ModuleNames to
83 -- Modules and ModLocations.
84 --
85 -- The Module contains one crucial bit of information about a module:
86 -- whether it lives in the current ("home") package or not (see Module
87 -- for more details).
88 --
89 -- The ModLocation contains the names of all the files associated with
90 -- that module: its source file, .hi file, object file, etc.
91
92 data FindResult
93   = Found ModLocation PackageIdH
94         -- the module was found
95   | PackageHidden PackageId
96         -- for an explicit source import: the package containing the module is
97         -- not exposed.
98   | ModuleHidden  PackageId
99         -- for an explicit source import: the package containing the module is
100         -- exposed, but the module itself is hidden.
101   | NotFound [FilePath]
102         -- the module was not found, the specified places were searched.
103
104 type LocalFindResult = MaybeErr [FilePath] FinderCacheEntry
105         -- LocalFindResult is used for internal functions which 
106         -- return a more informative type; it's munged into
107         -- the external FindResult by 'cached'
108
109 cached :: Bool
110        -> (DynFlags -> Module -> IO LocalFindResult)
111        -> DynFlags -> Module -> Bool -> IO FindResult
112 cached home_allowed wrapped_fn dflags name explicit 
113   = do  {       -- First try the cache
114           mb_entry <- lookupFinderCache name
115         ; case mb_entry of {
116             Just old_entry -> return (found old_entry) ;
117             Nothing    -> do
118
119         {       -- Now try the wrapped function
120           mb_entry <- wrapped_fn dflags name
121         ; case mb_entry of
122             Failed paths        -> return (NotFound paths)
123             Succeeded new_entry -> do { addToFinderCache name new_entry
124                                       ; return (found new_entry) }
125         }}} 
126   where
127         -- We've found the module, so the remaining question is
128         -- whether it's visible or not
129     found :: FinderCacheEntry -> FindResult
130     found (loc, Nothing)
131         | home_allowed  = Found loc HomePackage
132         | otherwise     = NotFound []
133     found (loc, Just (pkg, exposed_mod))
134         | explicit && not exposed_mod   = ModuleHidden pkg_name
135         | explicit && not (exposed pkg) = PackageHidden pkg_name
136         | otherwise                     = Found loc (ExtPackage (mkPackageId (package pkg)))
137         where
138           pkg_name = packageConfigId pkg
139
140 addHomeModuleToFinder :: Module -> ModLocation -> IO ()
141 addHomeModuleToFinder mod loc = addToFinderCache mod (loc, Nothing)
142
143
144 -- -----------------------------------------------------------------------------
145 --      The two external entry points
146
147
148 findModule :: DynFlags -> Module -> Bool -> IO FindResult
149 findModule = cached True findModule' 
150   
151 findPackageModule :: DynFlags -> 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 findLinkable :: Module -> ModLocation -> IO (Maybe Linkable)
397 findLinkable mod locn
398    = do let obj_fn = ml_obj_file locn
399         obj_exist <- doesFileExist obj_fn
400         if not obj_exist 
401          then return Nothing 
402          else 
403          do let stub_fn = case splitFilename3 obj_fn of
404                              (dir, base, ext) -> dir ++ "/" ++ base ++ "_stub.o"
405             stub_exist <- doesFileExist stub_fn
406             obj_time <- getModificationTime obj_fn
407             if stub_exist
408              then return (Just (LM obj_time mod [DotO obj_fn, DotO stub_fn]))
409              else return (Just (LM obj_time mod [DotO obj_fn]))
410
411 -- -----------------------------------------------------------------------------
412 -- Utils
413
414 dots_to_slashes = map (\c -> if c == '.' then '/' else c)
415
416
417 -- -----------------------------------------------------------------------------
418 -- Error messages
419
420 cantFindError :: DynFlags -> Module -> FindResult -> SDoc
421 cantFindError dflags mod_name find_result
422   = hang (ptext SLIT("Could not find module") <+> quotes (ppr mod_name) <> colon)
423        2 more_info
424   where
425     more_info
426       = case find_result of
427             PackageHidden pkg 
428                 -> ptext SLIT("it is a member of package") <+> ppr pkg <> comma
429                    <+> ptext SLIT("which is hidden")
430
431             ModuleHidden pkg
432                 -> ptext SLIT("it is hidden") <+> parens (ptext SLIT("in package")
433                    <+> ppr pkg)
434
435             NotFound files
436                 | null files
437                 -> ptext SLIT("it is not a module in the current program, or in any known package.")
438                 | verbosity dflags < 3 
439                 -> ptext SLIT("use -v to see a list of the files searched for")
440                 | otherwise 
441                 -> hang (ptext SLIT("locations searched:")) 
442                       2 (vcat (map text files))
443
444             Found _ _ -> panic "cantFindErr"
445 \end{code}