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