PageRenderTime 86ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/utils/ghctags/Main.hs

https://github.com/jberthold/ghc
Haskell | 364 lines | 277 code | 48 blank | 39 comment | 14 complexity | d01370fdb5f8a00260cffe08b865a9f8 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. XValBindsLR (NValBinds 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. XForeignDecl { } -> []
  235. in vals ++ tys ++ fors
  236. where found = foundOfLName mod
  237. startOfLocated :: Located a -> RealSrcLoc
  238. startOfLocated lHs = case getLoc lHs of
  239. RealSrcSpan l -> realSrcSpanStart l
  240. UnhelpfulSpan _ -> panic "startOfLocated UnhelpfulSpan"
  241. foundOfLName :: ModuleName -> Located Name -> FoundThing
  242. foundOfLName mod id = FoundThing mod (getOccString $ unLoc id) (startOfLocated id)
  243. boundThings :: ModuleName -> LHsBind GhcRn -> [FoundThing]
  244. boundThings modname lbinding =
  245. case unLoc lbinding of
  246. FunBind { fun_id = id } -> [thing id]
  247. PatBind { pat_lhs = lhs } -> patThings lhs []
  248. VarBind { var_id = id } -> [FoundThing modname (getOccString id) (startOfLocated lbinding)]
  249. AbsBinds { } -> [] -- nothing interesting in a type abstraction
  250. PatSynBind _ PSB{ psb_id = id } -> [thing id]
  251. PatSynBind _ (XPatSynBind _) -> []
  252. XHsBindsLR _ -> []
  253. where thing = foundOfLName modname
  254. patThings lpat tl =
  255. let loc = startOfLocated lpat
  256. lid id = FoundThing modname (getOccString id) loc
  257. in case unLoc lpat of
  258. WildPat _ -> tl
  259. VarPat _ (L _ name) -> lid name : tl
  260. LazyPat _ p -> patThings p tl
  261. AsPat _ id p -> patThings p (thing id : tl)
  262. ParPat _ p -> patThings p tl
  263. BangPat _ p -> patThings p tl
  264. ListPat _ ps -> foldr patThings tl ps
  265. TuplePat _ ps _ -> foldr patThings tl ps
  266. ConPatIn _ conargs -> conArgs conargs tl
  267. ConPatOut{ pat_args = conargs } -> conArgs conargs tl
  268. LitPat _ _ -> tl
  269. NPat {} -> tl -- form of literal pattern?
  270. NPlusKPat _ id _ _ _ _ -> thing id : tl
  271. SigPat _ p -> patThings p tl
  272. _ -> error "boundThings"
  273. conArgs (PrefixCon ps) tl = foldr patThings tl ps
  274. conArgs (RecCon (HsRecFields { rec_flds = flds })) tl
  275. = foldr (\(L _ f) tl' -> patThings (hsRecFieldArg f) tl') tl flds
  276. conArgs (InfixCon p1 p2) tl = patThings p1 $ patThings p2 tl
  277. -- stuff for dealing with ctags output format
  278. writeTagsData :: (Maybe Handle, Maybe Handle) -> FileData -> IO ()
  279. writeTagsData (mb_ctags_hdl, mb_etags_hdl) fd = do
  280. maybe (return ()) (\hdl -> writectagsfile hdl fd) mb_ctags_hdl
  281. maybe (return ()) (\hdl -> writeetagsfile hdl fd) mb_etags_hdl
  282. writectagsfile :: Handle -> FileData -> IO ()
  283. writectagsfile ctagsfile filedata = do
  284. let things = getfoundthings filedata
  285. mapM_ (\x -> hPutStrLn ctagsfile $ dumpthing False x) things
  286. mapM_ (\x -> hPutStrLn ctagsfile $ dumpthing True x) things
  287. getfoundthings :: FileData -> [FoundThing]
  288. getfoundthings (FileData _filename things _src_lines) = things
  289. dumpthing :: Bool -> FoundThing -> String
  290. dumpthing showmod (FoundThing modname name loc) =
  291. fullname ++ "\t" ++ filename ++ "\t" ++ (show line)
  292. where line = srcLocLine loc
  293. filename = unpackFS $ srcLocFile loc
  294. fullname = if showmod then moduleNameString modname ++ "." ++ name
  295. else name
  296. -- stuff for dealing with etags output format
  297. writeetagsfile :: Handle -> FileData -> IO ()
  298. writeetagsfile etagsfile = hPutStr etagsfile . e_dumpfiledata
  299. e_dumpfiledata :: FileData -> String
  300. e_dumpfiledata (FileData filename things line_map) =
  301. "\x0c\n" ++ filename ++ "," ++ (show thingslength) ++ "\n" ++ thingsdump
  302. where
  303. thingsdump = concat $ map (e_dumpthing line_map) things
  304. thingslength = length thingsdump
  305. e_dumpthing :: Map Int String -> FoundThing -> String
  306. e_dumpthing src_lines (FoundThing modname name loc) =
  307. tagline name ++ tagline (moduleNameString modname ++ "." ++ name)
  308. where tagline n = src_code ++ "\x7f"
  309. ++ n ++ "\x01"
  310. ++ (show line) ++ "," ++ (show $ column) ++ "\n"
  311. line = srcLocLine loc
  312. column = srcLocCol loc
  313. src_code = case M.lookup line src_lines of
  314. Just l -> take (column + length name) l
  315. Nothing -> --trace (show ("not found: ", moduleNameString modname, name, line, column))
  316. name