2 % (c) The University of Glasgow, 2000
4 \section[Finder]{Module Finder}
8 flushFinderCache, -- :: IO ()
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)
15 hiBootFilePath, -- :: ModLocation -> IO FilePath
16 hiBootExt, -- :: String
17 hiBootVerExt, -- :: String
21 #include "HsVersions.h"
24 import UniqFM ( filterUFM )
25 import HscTypes ( Linkable(..), Unlinked(..) )
32 import CmdLineOpts ( DynFlags(..) )
34 import DATA_IOREF ( IORef, writeIORef, readIORef )
37 import System.Directory
40 import Data.Maybe ( isNothing )
42 -- -----------------------------------------------------------------------------
45 -- The Finder provides a thin filesystem abstraction to the rest of
46 -- the compiler. For a given module, it can tell you where the
47 -- source, interface, and object files for that module live.
49 -- It does *not* know which particular package a module lives in. Use
50 -- Packages.moduleToPackageConfig for that.
52 -- -----------------------------------------------------------------------------
55 GLOBAL_VAR(finder_cache, emptyModuleEnv, ModuleEnv FinderCacheEntry)
57 type FinderCacheEntry = (ModLocation,Maybe (PackageConfig,Bool))
59 -- remove all the home modules from the cache; package modules are
60 -- assumed to not move around during a session.
61 flushFinderCache :: IO ()
63 fm <- readIORef finder_cache
64 writeIORef finder_cache (filterUFM (\(loc,m) -> isNothing m) fm)
66 addToFinderCache :: Module -> FinderCacheEntry -> IO ()
67 addToFinderCache mod_name entry = do
68 fm <- readIORef finder_cache
69 writeIORef finder_cache (extendModuleEnv fm mod_name entry)
71 lookupFinderCache :: Module -> IO (Maybe FinderCacheEntry)
72 lookupFinderCache mod_name = do
73 fm <- readIORef finder_cache
74 return $! lookupModuleEnv fm mod_name
76 -- -----------------------------------------------------------------------------
79 -- This is the main interface to the finder, which maps ModuleNames to
80 -- Modules and ModLocations.
82 -- The Module contains one crucial bit of information about a module:
83 -- whether it lives in the current ("home") package or not (see Module
86 -- The ModLocation contains the names of all the files associated with
87 -- that module: its source file, .hi file, object file, etc.
90 = Found ModLocation PackageIdH
91 -- the module was found
92 | PackageHidden PackageId
93 -- for an explicit source import: the package containing the module is
95 | ModuleHidden PackageId
96 -- for an explicit source import: the package containing the module is
97 -- exposed, but the module itself is hidden.
99 -- the module was not found, the specified places were searched.
101 findModule :: DynFlags -> Module -> Bool -> IO FindResult
102 findModule = cached findModule'
104 findModule' :: DynFlags -> Module -> Bool -> IO FindResult
105 findModule' dflags name explicit = do
106 r <- findPackageModule' dflags name explicit
108 NotFound pkg_files -> do
109 j <- maybeHomeModule dflags name
111 NotFound home_files ->
112 return (NotFound (home_files ++ pkg_files))
114 -> return other_result
116 -> return other_result
118 cached fn dflags name explicit = do
119 m <- lookupFinderCache name
121 Nothing -> fn dflags name explicit
123 | Just err <- visible explicit maybe_pkg -> return err
124 | otherwise -> return (Found loc (pkgInfoToId maybe_pkg))
126 pkgInfoToId :: Maybe (PackageConfig,Bool) -> PackageIdH
127 pkgInfoToId (Just (pkg,_)) = ExtPackage (mkPackageId (package pkg))
128 pkgInfoToId Nothing = HomePackage
130 -- Is a module visible or not? Returns Nothing if the import is ok,
131 -- or Just err if there's a visibility error.
132 visible :: Bool -> Maybe (PackageConfig,Bool) -> Maybe FindResult
133 visible explicit maybe_pkg
134 | Nothing <- maybe_pkg = Nothing -- home module ==> YES
135 | not explicit = Nothing -- implicit import ==> YES
136 | Just (pkg, exposed_module) <- maybe_pkg
138 _ | not exposed_module -> Just (ModuleHidden pkgname)
139 | not (exposed pkg) -> Just (PackageHidden pkgname)
140 | otherwise -> Nothing
142 pkgname = packageConfigId pkg
145 hiBootExt = "hi-boot"
146 hiBootVerExt = "hi-boot-" ++ cHscIfaceFileVersion
148 maybeHomeModule :: DynFlags -> Module -> IO FindResult
149 maybeHomeModule dflags mod = do
150 let home_path = importPaths dflags
151 hisuf <- readIORef v_Hi_suf
152 mode <- readIORef v_GhcMode
156 [ ("hs", mkHomeModLocationSearched mod)
157 , ("lhs", mkHomeModLocationSearched mod)
160 hi_exts = [ (hisuf, mkHiOnlyModLocation hisuf mod) ]
163 [ (hiBootVerExt, mkHiOnlyModLocation hisuf mod)
164 , (hiBootExt, mkHiOnlyModLocation hisuf mod)
167 -- In compilation manager modes, we look for source files in the home
168 -- package because we can compile these automatically. In one-shot
169 -- compilation mode we look for .hi and .hi-boot files only.
171 -- When generating dependencies, we're interested in either category.
174 | mode == DoMkDependHS = hi_exts ++ source_exts ++ boot_exts
175 | isCompManagerMode mode = source_exts
176 | otherwise {-one-shot-} = hi_exts ++ boot_exts
178 searchPathExts home_path mod exts
180 -- -----------------------------------------------------------------------------
181 -- Looking for a package module
183 findPackageModule :: DynFlags -> Module -> Bool -> IO FindResult
184 findPackageModule = cached findPackageModule'
186 findPackageModule' :: DynFlags -> Module -> Bool -> IO FindResult
187 findPackageModule' dflags mod explicit = do
188 mode <- readIORef v_GhcMode
190 case moduleToPackageConfig dflags mod of
191 Nothing -> return (NotFound [])
192 pkg_info@(Just (pkg_conf, module_exposed))
193 | Just err <- visible explicit pkg_info -> return err
194 | otherwise -> findPackageIface mode mod paths pkg_info
196 paths = importDirs pkg_conf
202 -> Maybe (PackageConfig,Bool)
204 findPackageIface mode mod imp_dirs pkg_info = do
205 -- hi-suffix for packages depends on the build tag.
207 do tag <- readIORef v_Build_tag
210 else return (tag ++ "_hi")
215 mkPackageModLocation pkg_info package_hisuf mod) ]
218 [ ("hs", mkPackageModLocation pkg_info package_hisuf mod)
219 , ("lhs", mkPackageModLocation pkg_info package_hisuf mod)
222 -- mkdependHS needs to look for source files in packages too, so
223 -- that we can make dependencies between package before they have
226 | mode == DoMkDependHS = hi_exts ++ source_exts
227 | otherwise = hi_exts
229 -- we never look for a .hi-boot file in an external package;
230 -- .hi-boot files only make sense for the home package.
231 searchPathExts imp_dirs mod exts
233 -- -----------------------------------------------------------------------------
234 -- General path searching
237 :: [FilePath] -- paths to search
238 -> Module -- module name
241 String -> String -> String -> IO FindResult -- action
246 searchPathExts path mod exts = search to_search
248 basename = dots_to_slashes (moduleUserString mod)
250 to_search :: [(FilePath, IO FindResult)]
251 to_search = [ (file, fn p basename ext)
254 let base | p == "." = basename
255 | otherwise = p ++ '/':basename
256 file = base ++ '.':ext
259 search [] = return (NotFound (map fst to_search))
260 search ((file, result) : rest) = do
261 b <- doesFileExist file
266 -- -----------------------------------------------------------------------------
267 -- Building ModLocations
269 mkHiOnlyModLocation hisuf mod path basename _ext = do
270 -- basename == dots_to_slashes (moduleNameUserString mod)
271 loc <- hiOnlyModLocation path basename hisuf
272 addToFinderCache mod (loc, Nothing)
273 return (Found loc HomePackage)
275 mkPackageModLocation pkg_info hisuf mod path basename _ext = do
276 -- basename == dots_to_slashes (moduleNameUserString mod)
277 loc <- hiOnlyModLocation path basename hisuf
278 addToFinderCache mod (loc, pkg_info)
279 return (Found loc (pkgInfoToId pkg_info))
281 hiOnlyModLocation path basename hisuf
282 = do let full_basename = path++'/':basename
283 obj_fn <- mkObjPath full_basename basename
284 return ModLocation{ ml_hspp_file = Nothing,
285 ml_hspp_buf = Nothing,
286 ml_hs_file = Nothing,
287 ml_hi_file = full_basename ++ '.':hisuf,
288 -- Remove the .hi-boot suffix from
289 -- hi_file, if it had one. We always
290 -- want the name of the real .hi file
291 -- in the ml_hi_file field.
295 -- -----------------------------------------------------------------------------
296 -- Constructing a home module location
298 -- This is where we construct the ModLocation for a module in the home
299 -- package, for which we have a source file. It is called from three
302 -- (a) Here in the finder, when we are searching for a module to import,
303 -- using the search path (-i option).
305 -- (b) The compilation manager, when constructing the ModLocation for
306 -- a "root" module (a source file named explicitly on the command line
307 -- or in a :load command in GHCi).
309 -- (c) The driver in one-shot mode, when we need to construct a
310 -- ModLocation for a source file named on the command-line.
315 -- The name of the module
318 -- (a): The search path component where the source file was found.
322 -- (a): dots_to_slashes (moduleNameUserString mod)
323 -- (b) and (c): The filename of the source file, minus its extension
326 -- The filename extension of the source file (usually "hs" or "lhs").
328 mkHomeModLocation mod src_filename = do
329 let (basename,extension) = splitFilename src_filename
330 mkHomeModLocation' mod basename extension
332 mkHomeModLocationSearched mod path basename ext = do
333 loc <- mkHomeModLocation' mod (path ++ '/':basename) ext
334 return (Found loc HomePackage)
336 mkHomeModLocation' mod src_basename ext = do
337 let mod_basename = dots_to_slashes (moduleUserString mod)
339 obj_fn <- mkObjPath src_basename mod_basename
340 hi_fn <- mkHiPath src_basename mod_basename
342 let loc = ModLocation{ ml_hspp_file = Nothing,
343 ml_hspp_buf = Nothing,
344 ml_hs_file = Just (src_basename ++ '.':ext),
346 ml_obj_file = obj_fn }
348 addToFinderCache mod (loc, Nothing)
351 -- | Constructs the filename of a .o file for a given source file.
352 -- Does /not/ check whether the .o file exists
354 :: FilePath -- the filename of the source file, minus the extension
355 -> String -- the module name with dots replaced by slashes
357 mkObjPath basename mod_basename
358 = do odir <- readIORef v_Output_dir
359 osuf <- readIORef v_Object_suf
361 let obj_basename | Just dir <- odir = dir ++ '/':mod_basename
362 | otherwise = basename
364 return (obj_basename ++ '.':osuf)
366 -- | Constructs the filename of a .hi file for a given source file.
367 -- Does /not/ check whether the .hi file exists
369 :: FilePath -- the filename of the source file, minus the extension
370 -> String -- the module name with dots replaced by slashes
372 mkHiPath basename mod_basename
373 = do hidir <- readIORef v_Hi_dir
374 hisuf <- readIORef v_Hi_suf
376 let hi_basename | Just dir <- hidir = dir ++ '/':mod_basename
377 | otherwise = basename
379 return (hi_basename ++ '.':hisuf)
383 hiBootFilePath :: ModLocation -> IO FilePath
384 -- Return Foo.hi-boot, or Foo.hi-boot-n, as appropriate
385 hiBootFilePath (ModLocation { ml_hi_file = hi_path })
386 = do { hi_ver_exists <- doesFileExist hi_boot_ver_path
387 ; if hi_ver_exists then return hi_boot_ver_path
388 else return hi_boot_path }
390 hi_boot_path = replaceFilenameSuffix hi_path hiBootExt ;
391 hi_boot_ver_path = replaceFilenameSuffix hi_path hiBootVerExt
394 -- -----------------------------------------------------------------------------
395 -- findLinkable isn't related to the other stuff in here,
396 -- but there's no other obvious place for it
398 findLinkable :: Module -> ModLocation -> IO (Maybe Linkable)
399 findLinkable mod locn
400 = do let obj_fn = ml_obj_file locn
401 obj_exist <- doesFileExist obj_fn
405 do let stub_fn = case splitFilename3 obj_fn of
406 (dir, base, ext) -> dir ++ "/" ++ base ++ "_stub.o"
407 stub_exist <- doesFileExist stub_fn
408 obj_time <- getModificationTime obj_fn
410 then return (Just (LM obj_time mod [DotO obj_fn, DotO stub_fn]))
411 else return (Just (LM obj_time mod [DotO obj_fn]))
413 -- -----------------------------------------------------------------------------
416 dots_to_slashes = map (\c -> if c == '.' then '/' else c)