| 1 | {-# OPTIONS -fno-cse #-} |
|---|
| 2 | -- -fno-cse is needed for GLOBAL_VAR's to behave properly |
|---|
| 3 | |
|---|
| 4 | ----------------------------------------------------------------------------- |
|---|
| 5 | -- |
|---|
| 6 | -- Makefile Dependency Generation |
|---|
| 7 | -- |
|---|
| 8 | -- (c) The University of Glasgow 2005 |
|---|
| 9 | -- |
|---|
| 10 | ----------------------------------------------------------------------------- |
|---|
| 11 | |
|---|
| 12 | module DriverMkDepend ( |
|---|
| 13 | doMkDependHS |
|---|
| 14 | ) where |
|---|
| 15 | |
|---|
| 16 | #include "HsVersions.h" |
|---|
| 17 | |
|---|
| 18 | import qualified GHC |
|---|
| 19 | import GhcMonad |
|---|
| 20 | import HsSyn ( ImportDecl(..) ) |
|---|
| 21 | import DynFlags |
|---|
| 22 | import Util |
|---|
| 23 | import HscTypes |
|---|
| 24 | import SysTools ( newTempName ) |
|---|
| 25 | import qualified SysTools |
|---|
| 26 | import Module |
|---|
| 27 | import Digraph ( SCC(..) ) |
|---|
| 28 | import Finder |
|---|
| 29 | import Outputable |
|---|
| 30 | import Panic |
|---|
| 31 | import SrcLoc |
|---|
| 32 | import Data.List |
|---|
| 33 | import FastString |
|---|
| 34 | |
|---|
| 35 | import Exception |
|---|
| 36 | import ErrUtils |
|---|
| 37 | |
|---|
| 38 | import System.Directory |
|---|
| 39 | import System.FilePath |
|---|
| 40 | import System.IO |
|---|
| 41 | import System.IO.Error ( isEOFError ) |
|---|
| 42 | import Control.Monad ( when ) |
|---|
| 43 | import Data.Maybe ( isJust ) |
|---|
| 44 | |
|---|
| 45 | ----------------------------------------------------------------- |
|---|
| 46 | -- |
|---|
| 47 | -- The main function |
|---|
| 48 | -- |
|---|
| 49 | ----------------------------------------------------------------- |
|---|
| 50 | |
|---|
| 51 | doMkDependHS :: GhcMonad m => [FilePath] -> m () |
|---|
| 52 | doMkDependHS srcs = do |
|---|
| 53 | -- Initialisation |
|---|
| 54 | dflags <- GHC.getSessionDynFlags |
|---|
| 55 | files <- liftIO $ beginMkDependHS dflags |
|---|
| 56 | |
|---|
| 57 | -- Do the downsweep to find all the modules |
|---|
| 58 | targets <- mapM (\s -> GHC.guessTarget s Nothing) srcs |
|---|
| 59 | GHC.setTargets targets |
|---|
| 60 | let excl_mods = depExcludeMods dflags |
|---|
| 61 | mod_summaries <- GHC.depanal excl_mods True {- Allow dup roots -} |
|---|
| 62 | |
|---|
| 63 | -- Sort into dependency order |
|---|
| 64 | -- There should be no cycles |
|---|
| 65 | let sorted = GHC.topSortModuleGraph False mod_summaries Nothing |
|---|
| 66 | |
|---|
| 67 | -- Print out the dependencies if wanted |
|---|
| 68 | liftIO $ debugTraceMsg dflags 2 (text "Module dependencies" $$ ppr sorted) |
|---|
| 69 | |
|---|
| 70 | -- Prcess them one by one, dumping results into makefile |
|---|
| 71 | -- and complaining about cycles |
|---|
| 72 | hsc_env <- getSession |
|---|
| 73 | root <- liftIO getCurrentDirectory |
|---|
| 74 | mapM_ (liftIO . processDeps dflags hsc_env excl_mods root (mkd_tmp_hdl files)) sorted |
|---|
| 75 | |
|---|
| 76 | -- If -ddump-mod-cycles, show cycles in the module graph |
|---|
| 77 | liftIO $ dumpModCycles dflags mod_summaries |
|---|
| 78 | |
|---|
| 79 | -- Tidy up |
|---|
| 80 | liftIO $ endMkDependHS dflags files |
|---|
| 81 | |
|---|
| 82 | -- Unconditional exiting is a bad idea. If an error occurs we'll get an |
|---|
| 83 | --exception; if that is not caught it's fine, but at least we have a |
|---|
| 84 | --chance to find out exactly what went wrong. Uncomment the following |
|---|
| 85 | --line if you disagree. |
|---|
| 86 | |
|---|
| 87 | --`GHC.ghcCatch` \_ -> io $ exitWith (ExitFailure 1) |
|---|
| 88 | |
|---|
| 89 | ----------------------------------------------------------------- |
|---|
| 90 | -- |
|---|
| 91 | -- beginMkDependHs |
|---|
| 92 | -- Create a temporary file, |
|---|
| 93 | -- find the Makefile, |
|---|
| 94 | -- slurp through it, etc |
|---|
| 95 | -- |
|---|
| 96 | ----------------------------------------------------------------- |
|---|
| 97 | |
|---|
| 98 | data MkDepFiles |
|---|
| 99 | = MkDep { mkd_make_file :: FilePath, -- Name of the makefile |
|---|
| 100 | mkd_make_hdl :: Maybe Handle, -- Handle for the open makefile |
|---|
| 101 | mkd_tmp_file :: FilePath, -- Name of the temporary file |
|---|
| 102 | mkd_tmp_hdl :: Handle } -- Handle of the open temporary file |
|---|
| 103 | |
|---|
| 104 | beginMkDependHS :: DynFlags -> IO MkDepFiles |
|---|
| 105 | beginMkDependHS dflags = do |
|---|
| 106 | -- open a new temp file in which to stuff the dependency info |
|---|
| 107 | -- as we go along. |
|---|
| 108 | tmp_file <- newTempName dflags "dep" |
|---|
| 109 | tmp_hdl <- openFile tmp_file WriteMode |
|---|
| 110 | |
|---|
| 111 | -- open the makefile |
|---|
| 112 | let makefile = depMakefile dflags |
|---|
| 113 | exists <- doesFileExist makefile |
|---|
| 114 | mb_make_hdl <- |
|---|
| 115 | if not exists |
|---|
| 116 | then return Nothing |
|---|
| 117 | else do |
|---|
| 118 | makefile_hdl <- openFile makefile ReadMode |
|---|
| 119 | |
|---|
| 120 | -- slurp through until we get the magic start string, |
|---|
| 121 | -- copying the contents into dep_makefile |
|---|
| 122 | let slurp = do |
|---|
| 123 | l <- hGetLine makefile_hdl |
|---|
| 124 | if (l == depStartMarker) |
|---|
| 125 | then return () |
|---|
| 126 | else do hPutStrLn tmp_hdl l; slurp |
|---|
| 127 | |
|---|
| 128 | -- slurp through until we get the magic end marker, |
|---|
| 129 | -- throwing away the contents |
|---|
| 130 | let chuck = do |
|---|
| 131 | l <- hGetLine makefile_hdl |
|---|
| 132 | if (l == depEndMarker) |
|---|
| 133 | then return () |
|---|
| 134 | else chuck |
|---|
| 135 | |
|---|
| 136 | catchIO slurp |
|---|
| 137 | (\e -> if isEOFError e then return () else ioError e) |
|---|
| 138 | catchIO chuck |
|---|
| 139 | (\e -> if isEOFError e then return () else ioError e) |
|---|
| 140 | |
|---|
| 141 | return (Just makefile_hdl) |
|---|
| 142 | |
|---|
| 143 | |
|---|
| 144 | -- write the magic marker into the tmp file |
|---|
| 145 | hPutStrLn tmp_hdl depStartMarker |
|---|
| 146 | |
|---|
| 147 | return (MkDep { mkd_make_file = makefile, mkd_make_hdl = mb_make_hdl, |
|---|
| 148 | mkd_tmp_file = tmp_file, mkd_tmp_hdl = tmp_hdl}) |
|---|
| 149 | |
|---|
| 150 | |
|---|
| 151 | ----------------------------------------------------------------- |
|---|
| 152 | -- |
|---|
| 153 | -- processDeps |
|---|
| 154 | -- |
|---|
| 155 | ----------------------------------------------------------------- |
|---|
| 156 | |
|---|
| 157 | processDeps :: DynFlags |
|---|
| 158 | -> HscEnv |
|---|
| 159 | -> [ModuleName] |
|---|
| 160 | -> FilePath |
|---|
| 161 | -> Handle -- Write dependencies to here |
|---|
| 162 | -> SCC ModSummary |
|---|
| 163 | -> IO () |
|---|
| 164 | -- Write suitable dependencies to handle |
|---|
| 165 | -- Always: |
|---|
| 166 | -- this.o : this.hs |
|---|
| 167 | -- |
|---|
| 168 | -- If the dependency is on something other than a .hi file: |
|---|
| 169 | -- this.o this.p_o ... : dep |
|---|
| 170 | -- otherwise |
|---|
| 171 | -- this.o ... : dep.hi |
|---|
| 172 | -- this.p_o ... : dep.p_hi |
|---|
| 173 | -- ... |
|---|
| 174 | -- (where .o is $osuf, and the other suffixes come from |
|---|
| 175 | -- the cmdline -s options). |
|---|
| 176 | -- |
|---|
| 177 | -- For {-# SOURCE #-} imports the "hi" will be "hi-boot". |
|---|
| 178 | |
|---|
| 179 | processDeps _ _ _ _ _ (CyclicSCC nodes) |
|---|
| 180 | = -- There shouldn't be any cycles; report them |
|---|
| 181 | ghcError (ProgramError (showSDoc $ GHC.cyclicModuleErr nodes)) |
|---|
| 182 | |
|---|
| 183 | processDeps dflags hsc_env excl_mods root hdl (AcyclicSCC node) |
|---|
| 184 | = do { let extra_suffixes = depSuffixes dflags |
|---|
| 185 | include_pkg_deps = depIncludePkgDeps dflags |
|---|
| 186 | src_file = msHsFilePath node |
|---|
| 187 | obj_file = msObjFilePath node |
|---|
| 188 | obj_files = insertSuffixes obj_file extra_suffixes |
|---|
| 189 | |
|---|
| 190 | do_imp loc is_boot pkg_qual imp_mod |
|---|
| 191 | = do { mb_hi <- findDependency hsc_env loc pkg_qual imp_mod |
|---|
| 192 | is_boot include_pkg_deps |
|---|
| 193 | ; case mb_hi of { |
|---|
| 194 | Nothing -> return () ; |
|---|
| 195 | Just hi_file -> do |
|---|
| 196 | { let hi_files = insertSuffixes hi_file extra_suffixes |
|---|
| 197 | write_dep (obj,hi) = writeDependency root hdl [obj] hi |
|---|
| 198 | |
|---|
| 199 | -- Add one dependency for each suffix; |
|---|
| 200 | -- e.g. A.o : B.hi |
|---|
| 201 | -- A.x_o : B.x_hi |
|---|
| 202 | ; mapM_ write_dep (obj_files `zip` hi_files) }}} |
|---|
| 203 | |
|---|
| 204 | |
|---|
| 205 | -- Emit std dependency of the object(s) on the source file |
|---|
| 206 | -- Something like A.o : A.hs |
|---|
| 207 | ; writeDependency root hdl obj_files src_file |
|---|
| 208 | |
|---|
| 209 | -- Emit a dependency for each import |
|---|
| 210 | |
|---|
| 211 | ; let do_imps is_boot idecls = sequence_ |
|---|
| 212 | [ do_imp loc is_boot (ideclPkgQual i) mod |
|---|
| 213 | | L loc i <- idecls, |
|---|
| 214 | let mod = unLoc (ideclName i), |
|---|
| 215 | mod `notElem` excl_mods ] |
|---|
| 216 | |
|---|
| 217 | ; do_imps True (ms_srcimps node) |
|---|
| 218 | ; do_imps False (ms_imps node) |
|---|
| 219 | } |
|---|
| 220 | |
|---|
| 221 | |
|---|
| 222 | findDependency :: HscEnv |
|---|
| 223 | -> SrcSpan |
|---|
| 224 | -> Maybe FastString -- package qualifier, if any |
|---|
| 225 | -> ModuleName -- Imported module |
|---|
| 226 | -> IsBootInterface -- Source import |
|---|
| 227 | -> Bool -- Record dependency on package modules |
|---|
| 228 | -> IO (Maybe FilePath) -- Interface file file |
|---|
| 229 | findDependency hsc_env srcloc pkg imp is_boot include_pkg_deps |
|---|
| 230 | = do { -- Find the module; this will be fast because |
|---|
| 231 | -- we've done it once during downsweep |
|---|
| 232 | r <- findImportedModule hsc_env imp pkg |
|---|
| 233 | ; case r of |
|---|
| 234 | Found loc _ |
|---|
| 235 | -- Home package: just depend on the .hi or hi-boot file |
|---|
| 236 | | isJust (ml_hs_file loc) || include_pkg_deps |
|---|
| 237 | -> return (Just (addBootSuffix_maybe is_boot (ml_hi_file loc))) |
|---|
| 238 | |
|---|
| 239 | -- Not in this package: we don't need a dependency |
|---|
| 240 | | otherwise |
|---|
| 241 | -> return Nothing |
|---|
| 242 | |
|---|
| 243 | fail -> throwOneError $ mkPlainErrMsg srcloc $ |
|---|
| 244 | cannotFindModule (hsc_dflags hsc_env) imp fail |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | ----------------------------- |
|---|
| 248 | writeDependency :: FilePath -> Handle -> [FilePath] -> FilePath -> IO () |
|---|
| 249 | -- (writeDependency r h [t1,t2] dep) writes to handle h the dependency |
|---|
| 250 | -- t1 t2 : dep |
|---|
| 251 | writeDependency root hdl targets dep |
|---|
| 252 | = do let -- We need to avoid making deps on |
|---|
| 253 | -- c:/foo/... |
|---|
| 254 | -- on cygwin as make gets confused by the : |
|---|
| 255 | -- Making relative deps avoids some instances of this. |
|---|
| 256 | dep' = makeRelative root dep |
|---|
| 257 | forOutput = escapeSpaces . reslash Forwards . normalise |
|---|
| 258 | output = unwords (map forOutput targets) ++ " : " ++ forOutput dep' |
|---|
| 259 | hPutStrLn hdl output |
|---|
| 260 | |
|---|
| 261 | ----------------------------- |
|---|
| 262 | insertSuffixes |
|---|
| 263 | :: FilePath -- Original filename; e.g. "foo.o" |
|---|
| 264 | -> [String] -- Extra suffices e.g. ["x","y"] |
|---|
| 265 | -> [FilePath] -- Zapped filenames e.g. ["foo.o", "foo.x_o", "foo.y_o"] |
|---|
| 266 | -- Note that that the extra bit gets inserted *before* the old suffix |
|---|
| 267 | -- We assume the old suffix contains no dots, so we can strip it with removeSuffix |
|---|
| 268 | |
|---|
| 269 | -- NOTE: we used to have this comment |
|---|
| 270 | -- In order to construct hi files with alternate suffixes, we |
|---|
| 271 | -- now have to find the "basename" of the hi file. This is |
|---|
| 272 | -- difficult because we can't just split the hi filename |
|---|
| 273 | -- at the last dot - the hisuf might have dots in it. So we |
|---|
| 274 | -- check whether the hi filename ends in hisuf, and if it does, |
|---|
| 275 | -- we strip off hisuf, otherwise we strip everything after the |
|---|
| 276 | -- last dot. |
|---|
| 277 | -- But I'm not sure we care about hisufs with dots in them. |
|---|
| 278 | -- Lots of other things will break first! |
|---|
| 279 | |
|---|
| 280 | insertSuffixes file_name extras |
|---|
| 281 | = file_name : [ basename <.> (extra ++ "_" ++ suffix) | extra <- extras ] |
|---|
| 282 | where |
|---|
| 283 | (basename, suffix) = case splitExtension file_name of |
|---|
| 284 | -- Drop the "." from the extension |
|---|
| 285 | (b, s) -> (b, drop 1 s) |
|---|
| 286 | |
|---|
| 287 | |
|---|
| 288 | ----------------------------------------------------------------- |
|---|
| 289 | -- |
|---|
| 290 | -- endMkDependHs |
|---|
| 291 | -- Complete the makefile, close the tmp file etc |
|---|
| 292 | -- |
|---|
| 293 | ----------------------------------------------------------------- |
|---|
| 294 | |
|---|
| 295 | endMkDependHS :: DynFlags -> MkDepFiles -> IO () |
|---|
| 296 | |
|---|
| 297 | endMkDependHS dflags |
|---|
| 298 | (MkDep { mkd_make_file = makefile, mkd_make_hdl = makefile_hdl, |
|---|
| 299 | mkd_tmp_file = tmp_file, mkd_tmp_hdl = tmp_hdl }) |
|---|
| 300 | = do |
|---|
| 301 | -- write the magic marker into the tmp file |
|---|
| 302 | hPutStrLn tmp_hdl depEndMarker |
|---|
| 303 | |
|---|
| 304 | case makefile_hdl of |
|---|
| 305 | Nothing -> return () |
|---|
| 306 | Just hdl -> do |
|---|
| 307 | |
|---|
| 308 | -- slurp the rest of the original makefile and copy it into the output |
|---|
| 309 | let slurp = do |
|---|
| 310 | l <- hGetLine hdl |
|---|
| 311 | hPutStrLn tmp_hdl l |
|---|
| 312 | slurp |
|---|
| 313 | |
|---|
| 314 | catchIO slurp |
|---|
| 315 | (\e -> if isEOFError e then return () else ioError e) |
|---|
| 316 | |
|---|
| 317 | hClose hdl |
|---|
| 318 | |
|---|
| 319 | hClose tmp_hdl -- make sure it's flushed |
|---|
| 320 | |
|---|
| 321 | -- Create a backup of the original makefile |
|---|
| 322 | when (isJust makefile_hdl) |
|---|
| 323 | (SysTools.copy dflags ("Backing up " ++ makefile) |
|---|
| 324 | makefile (makefile++".bak")) |
|---|
| 325 | |
|---|
| 326 | -- Copy the new makefile in place |
|---|
| 327 | SysTools.copy dflags "Installing new makefile" tmp_file makefile |
|---|
| 328 | |
|---|
| 329 | |
|---|
| 330 | ----------------------------------------------------------------- |
|---|
| 331 | -- Module cycles |
|---|
| 332 | ----------------------------------------------------------------- |
|---|
| 333 | |
|---|
| 334 | dumpModCycles :: DynFlags -> [ModSummary] -> IO () |
|---|
| 335 | dumpModCycles dflags mod_summaries |
|---|
| 336 | | not (dopt Opt_D_dump_mod_cycles dflags) |
|---|
| 337 | = return () |
|---|
| 338 | |
|---|
| 339 | | null cycles |
|---|
| 340 | = putMsg dflags (ptext (sLit "No module cycles")) |
|---|
| 341 | |
|---|
| 342 | | otherwise |
|---|
| 343 | = putMsg dflags (hang (ptext (sLit "Module cycles found:")) 2 pp_cycles) |
|---|
| 344 | where |
|---|
| 345 | |
|---|
| 346 | cycles :: [[ModSummary]] |
|---|
| 347 | cycles = [ c | CyclicSCC c <- GHC.topSortModuleGraph True mod_summaries Nothing ] |
|---|
| 348 | |
|---|
| 349 | pp_cycles = vcat [ (ptext (sLit "---------- Cycle") <+> int n <+> ptext (sLit "----------")) |
|---|
| 350 | $$ pprCycle c $$ blankLine |
|---|
| 351 | | (n,c) <- [1..] `zip` cycles ] |
|---|
| 352 | |
|---|
| 353 | pprCycle :: [ModSummary] -> SDoc |
|---|
| 354 | -- Print a cycle, but show only the imports within the cycle |
|---|
| 355 | pprCycle summaries = pp_group (CyclicSCC summaries) |
|---|
| 356 | where |
|---|
| 357 | cycle_mods :: [ModuleName] -- The modules in this cycle |
|---|
| 358 | cycle_mods = map (moduleName . ms_mod) summaries |
|---|
| 359 | |
|---|
| 360 | pp_group (AcyclicSCC ms) = pp_ms ms |
|---|
| 361 | pp_group (CyclicSCC mss) |
|---|
| 362 | = ASSERT( not (null boot_only) ) |
|---|
| 363 | -- The boot-only list must be non-empty, else there would |
|---|
| 364 | -- be an infinite chain of non-boot imoprts, and we've |
|---|
| 365 | -- already checked for that in processModDeps |
|---|
| 366 | pp_ms loop_breaker $$ vcat (map pp_group groups) |
|---|
| 367 | where |
|---|
| 368 | (boot_only, others) = partition is_boot_only mss |
|---|
| 369 | is_boot_only ms = not (any in_group (map (ideclName.unLoc) (ms_imps ms))) |
|---|
| 370 | in_group (L _ m) = m `elem` group_mods |
|---|
| 371 | group_mods = map (moduleName . ms_mod) mss |
|---|
| 372 | |
|---|
| 373 | loop_breaker = head boot_only |
|---|
| 374 | all_others = tail boot_only ++ others |
|---|
| 375 | groups = GHC.topSortModuleGraph True all_others Nothing |
|---|
| 376 | |
|---|
| 377 | pp_ms summary = text mod_str <> text (take (20 - length mod_str) (repeat ' ')) |
|---|
| 378 | <+> (pp_imps empty (map (ideclName.unLoc) (ms_imps summary)) $$ |
|---|
| 379 | pp_imps (ptext (sLit "{-# SOURCE #-}")) (map (ideclName.unLoc) (ms_srcimps summary))) |
|---|
| 380 | where |
|---|
| 381 | mod_str = moduleNameString (moduleName (ms_mod summary)) |
|---|
| 382 | |
|---|
| 383 | pp_imps :: SDoc -> [Located ModuleName] -> SDoc |
|---|
| 384 | pp_imps _ [] = empty |
|---|
| 385 | pp_imps what lms |
|---|
| 386 | = case [m | L _ m <- lms, m `elem` cycle_mods] of |
|---|
| 387 | [] -> empty |
|---|
| 388 | ms -> what <+> ptext (sLit "imports") <+> |
|---|
| 389 | pprWithCommas ppr ms |
|---|
| 390 | |
|---|
| 391 | ----------------------------------------------------------------- |
|---|
| 392 | -- |
|---|
| 393 | -- Flags |
|---|
| 394 | -- |
|---|
| 395 | ----------------------------------------------------------------- |
|---|
| 396 | |
|---|
| 397 | depStartMarker, depEndMarker :: String |
|---|
| 398 | depStartMarker = "# DO NOT DELETE: Beginning of Haskell dependencies" |
|---|
| 399 | depEndMarker = "# DO NOT DELETE: End of Haskell dependencies" |
|---|