PageRenderTime 55ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/utils/ghctags/Main.hs

https://github.com/ezyang/ghc
Haskell | 363 lines | 276 code | 48 blank | 39 comment | 14 complexity | a4131b9515e4b10f01044ea0b313483a MD5 | raw file
  1. {-# LANGUAGE ScopedTypeVariables #-}
  2. {-# LANGUAGE TypeFamilies #-}
  3. module Main where
  4. import Prelude hiding ( mod, id, mapM )
  5. import GHC
  6. --import Packages
  7. import HscTypes ( isBootSummary )
  8. import Digraph ( flattenSCCs )
  9. import DriverPhases ( isHaskellSrcFilename )
  10. import HscTypes ( msHsFilePath )
  11. import Name ( getOccString )
  12. --import ErrUtils ( printBagOfErrors )
  13. import Panic ( panic )
  14. import CmdLineParser (warnMsg)
  15. import DynFlags ( defaultFatalMessager, defaultFlushOut )
  16. import Bag
  17. import Exception
  18. import FastString
  19. import MonadUtils ( liftIO )
  20. import SrcLoc
  21. import Distribution.Simple.GHC ( componentGhcOptions )
  22. import Distribution.Simple.Configure ( getPersistBuildConfig )
  23. import Distribution.Simple.Program.GHC ( renderGhcOptions )
  24. import Distribution.PackageDescription ( libBuildInfo )
  25. import Distribution.Simple.LocalBuildInfo
  26. import Distribution.Types.LocalBuildInfo ( componentNameTargets' )
  27. import Distribution.Types.TargetInfo
  28. import qualified Distribution.Verbosity as V
  29. import Control.Monad hiding (mapM)
  30. import System.Environment
  31. import System.Console.GetOpt
  32. import System.Exit
  33. import System.IO
  34. import Data.List as List hiding ( group )
  35. import Data.Traversable (mapM)
  36. import Data.Map ( Map )
  37. import qualified Data.Map as M
  38. --import UniqFM
  39. --import Debug.Trace
  40. -- search for definitions of things
  41. -- we do this by parsing the source and grabbing top-level definitions
  42. -- We generate both CTAGS and ETAGS format tags files
  43. -- The former is for use in most sensible editors, while EMACS uses ETAGS
  44. ----------------------------------
  45. ---- CENTRAL DATA TYPES ----------
  46. type FileName = String
  47. type ThingName = String -- name of a defined entity in a Haskell program
  48. -- A definition we have found (we know its containing module, name, and location)
  49. data FoundThing = FoundThing ModuleName ThingName RealSrcLoc
  50. -- Data we have obtained from a file (list of things we found)
  51. data FileData = FileData FileName [FoundThing] (Map Int String)
  52. --- invariant (not checked): every found thing has a source location in that file?
  53. ------------------------------
  54. -------- MAIN PROGRAM --------
  55. main :: IO ()
  56. main = do
  57. progName <- getProgName
  58. let usageString =
  59. "Usage: " ++ progName ++ " [OPTION...] [-- GHC OPTION... --] [files...]"
  60. args <- getArgs
  61. let (ghcArgs', ourArgs, unbalanced) = splitArgs args
  62. let (flags, filenames, errs) = getOpt Permute options ourArgs
  63. let (hsfiles, otherfiles) = List.partition isHaskellSrcFilename filenames
  64. let ghc_topdir = case [ d | FlagTopDir d <- flags ] of
  65. [] -> ""
  66. (x:_) -> x
  67. mapM_ (\n -> putStr $ "Warning: ignoring non-Haskellish file " ++ n ++ "\n")
  68. otherfiles
  69. if unbalanced || errs /= [] || elem FlagHelp flags || hsfiles == []
  70. then do
  71. putStr $ unlines errs
  72. putStr $ usageInfo usageString options
  73. exitWith (ExitFailure 1)
  74. else return ()
  75. ghcArgs <- case [ d | FlagUseCabalConfig d <- flags ] of
  76. [distPref] -> do
  77. cabalOpts <- flagsFromCabal distPref
  78. return (cabalOpts ++ ghcArgs')
  79. [] ->
  80. return ghcArgs'
  81. _ -> error "Too many --use-cabal-config flags"
  82. print ghcArgs
  83. let modes = getMode flags
  84. let openFileMode = if elem FlagAppend flags
  85. then AppendMode
  86. else WriteMode
  87. ctags_hdl <- if CTags `elem` modes
  88. then Just `liftM` openFile "tags" openFileMode
  89. else return Nothing
  90. etags_hdl <- if ETags `elem` modes
  91. then Just `liftM` openFile "TAGS" openFileMode
  92. else return Nothing
  93. GHC.defaultErrorHandler defaultFatalMessager defaultFlushOut $
  94. runGhc (Just ghc_topdir) $ do
  95. --liftIO $ print "starting up session"
  96. dflags <- getSessionDynFlags
  97. (pflags, unrec, warns) <- parseDynamicFlags dflags{ verbosity=1 }
  98. (map noLoc ghcArgs)
  99. unless (null unrec) $
  100. liftIO $ putStrLn $ "Unrecognised options:\n" ++ show (map unLoc unrec)
  101. liftIO $ mapM_ putStrLn (map (unLoc . warnMsg) warns)
  102. let dflags2 = pflags { hscTarget = HscNothing } -- don't generate anything
  103. -- liftIO $ print ("pkgDB", case (pkgDatabase dflags2) of Nothing -> 0
  104. -- Just m -> sizeUFM m)
  105. _ <- setSessionDynFlags dflags2
  106. --liftIO $ print (length pkgs)
  107. targetsAtOneGo hsfiles (ctags_hdl,etags_hdl)
  108. mapM_ (mapM (liftIO . hClose)) [ctags_hdl, etags_hdl]
  109. ----------------------------------------------
  110. ---------- ARGUMENT PROCESSING --------------
  111. data Flag
  112. = FlagETags
  113. | FlagCTags
  114. | FlagBoth
  115. | FlagAppend
  116. | FlagHelp
  117. | FlagTopDir FilePath
  118. | FlagUseCabalConfig FilePath
  119. | FlagFilesFromCabal
  120. deriving (Ord, Eq, Show)
  121. -- ^Represents options passed to the program
  122. data Mode = ETags | CTags deriving Eq
  123. getMode :: [Flag] -> [Mode]
  124. getMode fs = go (concatMap modeLike fs)
  125. where go [] = [ETags,CTags]
  126. go [x] = [x]
  127. go more = nub more
  128. modeLike FlagETags = [ETags]
  129. modeLike FlagCTags = [CTags]
  130. modeLike FlagBoth = [ETags,CTags]
  131. modeLike _ = []
  132. splitArgs :: [String] -> ([String], [String], Bool)
  133. -- ^Pull out arguments between -- for GHC
  134. splitArgs args0 = split [] [] False args0
  135. where split ghc' tags' unbal ("--" : args) = split tags' ghc' (not unbal) args
  136. split ghc' tags' unbal (arg : args) = split ghc' (arg:tags') unbal args
  137. split ghc' tags' unbal [] = (reverse ghc', reverse tags', unbal)
  138. options :: [OptDescr Flag]
  139. -- supports getopt
  140. options = [ Option "" ["topdir"]
  141. (ReqArg FlagTopDir "DIR") "root of GHC installation (optional)"
  142. , Option "c" ["ctags"]
  143. (NoArg FlagCTags) "generate CTAGS file (ctags)"
  144. , Option "e" ["etags"]
  145. (NoArg FlagETags) "generate ETAGS file (etags)"
  146. , Option "b" ["both"]
  147. (NoArg FlagBoth) ("generate both CTAGS and ETAGS")
  148. , Option "a" ["append"]
  149. (NoArg FlagAppend) ("append to existing CTAGS and/or ETAGS file(s)")
  150. , Option "" ["use-cabal-config"]
  151. (ReqArg FlagUseCabalConfig "DIR") "use local cabal configuration from dist dir"
  152. , Option "" ["files-from-cabal"]
  153. (NoArg FlagFilesFromCabal) "use files from cabal"
  154. , Option "h" ["help"] (NoArg FlagHelp) "This help"
  155. ]
  156. flagsFromCabal :: FilePath -> IO [String]
  157. flagsFromCabal distPref = do
  158. lbi <- getPersistBuildConfig distPref
  159. let pd = localPkgDescr lbi
  160. case componentNameTargets' pd lbi CLibName of
  161. [target] ->
  162. let clbi = targetCLBI target
  163. CLib lib = getComponent pd (componentLocalName clbi)
  164. bi = libBuildInfo lib
  165. odir = buildDir lbi
  166. opts = componentGhcOptions V.normal lbi bi clbi odir
  167. in return $ renderGhcOptions (compiler lbi) (hostPlatform lbi) opts
  168. [] -> error "no library"
  169. _ -> error "more libraries than we know how to handle"
  170. ----------------------------------------------------------------
  171. --- LOADING HASKELL SOURCE
  172. --- (these bits actually run the compiler and produce abstract syntax)
  173. safeLoad :: LoadHowMuch -> Ghc SuccessFlag
  174. -- like GHC.load, but does not stop process on exception
  175. safeLoad mode = do
  176. _dflags <- getSessionDynFlags
  177. ghandle (\(e :: SomeException) -> liftIO (print e) >> return Failed ) $
  178. handleSourceError (\e -> printException e >> return Failed) $
  179. load mode
  180. targetsAtOneGo :: [FileName] -> (Maybe Handle, Maybe Handle) -> Ghc ()
  181. -- load a list of targets
  182. targetsAtOneGo hsfiles handles = do
  183. targets <- mapM (\f -> guessTarget f Nothing) hsfiles
  184. setTargets targets
  185. modgraph <- depanal [] False
  186. let mods = flattenSCCs $ topSortModuleGraph False modgraph Nothing
  187. graphData mods handles
  188. fileTarget :: FileName -> Target
  189. fileTarget filename = Target (TargetFile filename Nothing) True Nothing
  190. ---------------------------------------------------------------
  191. ----- CRAWLING ABSTRACT SYNTAX TO SNAFFLE THE DEFINITIONS -----
  192. graphData :: [ModSummary] -> (Maybe Handle, Maybe Handle) -> Ghc ()
  193. graphData mss handles = do
  194. mapM_ foundthings mss
  195. where foundthings ms =
  196. let filename = msHsFilePath ms
  197. modname = moduleName $ ms_mod ms
  198. in handleSourceError (\e -> do
  199. printException e
  200. liftIO $ exitWith (ExitFailure 1)) $
  201. do liftIO $ putStrLn ("loading " ++ filename)
  202. mod <- loadModule =<< typecheckModule =<< parseModule ms
  203. case mod of
  204. _ | isBootSummary ms -> return ()
  205. _ | Just s <- renamedSource mod ->
  206. liftIO (writeTagsData handles =<< fileData filename modname s)
  207. _otherwise ->
  208. liftIO $ exitWith (ExitFailure 1)
  209. fileData :: FileName -> ModuleName -> RenamedSource -> IO FileData
  210. fileData filename modname (group, _imports, _lie, _doc) = do
  211. -- lie is related to type checking and so is irrelevant
  212. -- imports contains import declarations and no definitions
  213. -- doc and haddock seem haddock-related; let's hope to ignore them
  214. ls <- lines `fmap` readFile filename
  215. let line_map = M.fromAscList $ zip [1..] ls
  216. line_map' <- evaluate line_map
  217. return $ FileData filename (boundValues modname group) line_map'
  218. boundValues :: ModuleName -> HsGroup GhcRn -> [FoundThing]
  219. -- ^Finds all the top-level definitions in a module
  220. boundValues mod group =
  221. let vals = case hs_valds group of
  222. ValBindsOut nest _sigs ->
  223. [ x | (_rec, binds) <- nest
  224. , bind <- bagToList binds
  225. , x <- boundThings mod bind ]
  226. _other -> error "boundValues"
  227. tys = [ n | ns <- map (fst . hsLTyClDeclBinders)
  228. (hs_tyclds group >>= group_tyclds)
  229. , n <- map found ns ]
  230. fors = concat $ map forBound (hs_fords group)
  231. where forBound lford = case unLoc lford of
  232. ForeignImport n _ _ _ -> [found n]
  233. ForeignExport { } -> []
  234. in vals ++ tys ++ fors
  235. where found = foundOfLName mod
  236. startOfLocated :: Located a -> RealSrcLoc
  237. startOfLocated lHs = case getLoc lHs of
  238. RealSrcSpan l -> realSrcSpanStart l
  239. UnhelpfulSpan _ -> panic "startOfLocated UnhelpfulSpan"
  240. foundOfLName :: ModuleName -> Located Name -> FoundThing
  241. foundOfLName mod id = FoundThing mod (getOccString $ unLoc id) (startOfLocated id)
  242. boundThings :: ModuleName -> LHsBind GhcRn -> [FoundThing]
  243. boundThings modname lbinding =
  244. case unLoc lbinding of
  245. FunBind { fun_id = id } -> [thing id]
  246. PatBind { pat_lhs = lhs } -> patThings lhs []
  247. VarBind { var_id = id } -> [FoundThing modname (getOccString id) (startOfLocated lbinding)]
  248. AbsBinds { } -> [] -- nothing interesting in a type abstraction
  249. PatSynBind PSB{ psb_id = id } -> [thing id]
  250. where thing = foundOfLName modname
  251. patThings lpat tl =
  252. let loc = startOfLocated lpat
  253. lid id = FoundThing modname (getOccString id) loc
  254. in case unLoc lpat of
  255. WildPat _ -> tl
  256. VarPat (L _ name) -> lid name : tl
  257. LazyPat p -> patThings p tl
  258. AsPat id p -> patThings p (thing id : tl)
  259. ParPat p -> patThings p tl
  260. BangPat p -> patThings p tl
  261. ListPat ps _ _ -> foldr patThings tl ps
  262. TuplePat ps _ _ -> foldr patThings tl ps
  263. PArrPat ps _ -> foldr patThings tl ps
  264. ConPatIn _ conargs -> conArgs conargs tl
  265. ConPatOut{ pat_args = conargs } -> conArgs conargs tl
  266. LitPat _ -> tl
  267. NPat {} -> tl -- form of literal pattern?
  268. NPlusKPat id _ _ _ _ _ -> thing id : tl
  269. SigPatIn p _ -> patThings p tl
  270. SigPatOut p _ -> patThings p tl
  271. _ -> error "boundThings"
  272. conArgs (PrefixCon ps) tl = foldr patThings tl ps
  273. conArgs (RecCon (HsRecFields { rec_flds = flds })) tl
  274. = foldr (\(L _ f) tl' -> patThings (hsRecFieldArg f) tl') tl flds
  275. conArgs (InfixCon p1 p2) tl = patThings p1 $ patThings p2 tl
  276. -- stuff for dealing with ctags output format
  277. writeTagsData :: (Maybe Handle, Maybe Handle) -> FileData -> IO ()
  278. writeTagsData (mb_ctags_hdl, mb_etags_hdl) fd = do
  279. maybe (return ()) (\hdl -> writectagsfile hdl fd) mb_ctags_hdl
  280. maybe (return ()) (\hdl -> writeetagsfile hdl fd) mb_etags_hdl
  281. writectagsfile :: Handle -> FileData -> IO ()
  282. writectagsfile ctagsfile filedata = do
  283. let things = getfoundthings filedata
  284. mapM_ (\x -> hPutStrLn ctagsfile $ dumpthing False x) things
  285. mapM_ (\x -> hPutStrLn ctagsfile $ dumpthing True x) things
  286. getfoundthings :: FileData -> [FoundThing]
  287. getfoundthings (FileData _filename things _src_lines) = things
  288. dumpthing :: Bool -> FoundThing -> String
  289. dumpthing showmod (FoundThing modname name loc) =
  290. fullname ++ "\t" ++ filename ++ "\t" ++ (show line)
  291. where line = srcLocLine loc
  292. filename = unpackFS $ srcLocFile loc
  293. fullname = if showmod then moduleNameString modname ++ "." ++ name
  294. else name
  295. -- stuff for dealing with etags output format
  296. writeetagsfile :: Handle -> FileData -> IO ()
  297. writeetagsfile etagsfile = hPutStr etagsfile . e_dumpfiledata
  298. e_dumpfiledata :: FileData -> String
  299. e_dumpfiledata (FileData filename things line_map) =
  300. "\x0c\n" ++ filename ++ "," ++ (show thingslength) ++ "\n" ++ thingsdump
  301. where
  302. thingsdump = concat $ map (e_dumpthing line_map) things
  303. thingslength = length thingsdump
  304. e_dumpthing :: Map Int String -> FoundThing -> String
  305. e_dumpthing src_lines (FoundThing modname name loc) =
  306. tagline name ++ tagline (moduleNameString modname ++ "." ++ name)
  307. where tagline n = src_code ++ "\x7f"
  308. ++ n ++ "\x01"
  309. ++ (show line) ++ "," ++ (show $ column) ++ "\n"
  310. line = srcLocLine loc
  311. column = srcLocCol loc
  312. src_code = case M.lookup line src_lines of
  313. Just l -> take (column + length name) l
  314. Nothing -> --trace (show ("not found: ", moduleNameString modname, name, line, column))
  315. name