{-# OPTIONS_GHC -fglasgow-exts #-} -- -- Haddock - A Haskell Documentation Tool -- -- (c) Simon Marlow 2003 -- module Main (main) where import HaddockHtml import HaddockHoogle import HaddockRename import HaddockTypes import HaddockUtil import HaddockVersion import Paths_haddock ( getDataDir ) import Prelude hiding ( catch ) import Control.Exception ( bracket, throwIO, catch, Exception(..) ) import Control.Monad ( when, liftM ) import Control.Monad.Writer ( Writer, runWriter, tell ) import Data.Char ( isSpace ) import Data.IORef ( writeIORef ) import Data.List ( nub, nubBy, (\\), foldl', sortBy, foldl1, init, mapAccumL, find, isPrefixOf ) import Data.Maybe ( Maybe(..), isJust, isNothing, maybeToList, listToMaybe, fromJust, catMaybes ) import System.Console.GetOpt ( getOpt, usageInfo, ArgOrder(..), OptDescr(..), ArgDescr(..) ) import System.Environment ( getArgs ) import System.IO ( stderr, IOMode(..), openFile, hClose, hGetContents, hPutStrLn ) import System.Directory ( doesFileExist, doesDirectoryExist ) import qualified Data.Map as Map import Data.Map (Map) import Distribution.InstalledPackageInfo ( InstalledPackageInfo(..) ) import qualified GHC ( init ) import GHC hiding ( init ) import Outputable import SrcLoc import qualified Digraph as Digraph import Name import Module ( mkModule ) import InstEnv import Class import TypeRep import Var hiding ( varName ) import TyCon import PrelNames import FastString #define FSLIT(x) (mkFastString# (x#)) import DynFlags hiding ( Option ) import Packages hiding ( package ) import StaticFlags ( parseStaticFlags ) import Unique ( mkUnique ) ----------------------------------------------------------------------------- -- Top-level stuff type CheckedMod = (Module, FullyCheckedMod, FilePath) main :: IO () main = do -- first, get the GHC library dir (-B option) args <- getArgs (libDir, rest) <- getLibDir args -- find out which flag mode we are in let (isGHCMode, rest') = parseModeFlag rest -- initialize GHC (session, dynflags) <- startGHC libDir -- parse GHC flags given to the program (dynflags', rest'') <- if isGHCMode then parseGHCFlags_GHCMode dynflags rest' else parseGHCFlags_HaddockMode dynflags rest' setSessionDynFlags session dynflags' -- parse Haddock specific flags (flags, fileArgs) <- parseHaddockOpts rest'' -- try to sort and check the input files using the GHC API modules <- sortAndCheckModules session dynflags' fileArgs -- create a PackageData for each external package in the session -- using the GHC API. The PackageData contains an html path, -- a doc env and a list of module names. packages <- getPackages session dynflags' -- update the html references (module -> html file mapping) updateHTMLXRefs packages -- combine the doc envs of the external packages into one let env = packagesDocEnv packages -- TODO: continue to break up the run function into parts run flags modules env parseModeFlag :: [String] -> (Bool, [String]) parseModeFlag ("--ghc-flags":rest) = (True, rest) parseModeFlag rest = (False, rest) parseGHCFlags_GHCMode :: DynFlags -> [String] -> IO (DynFlags, [String]) parseGHCFlags_GHCMode dynflags args = do (dynflags', rest) <- parseDynamicFlags dynflags args rest' <- parseStaticFlags rest return (dynflags', rest') parseGHCFlags_HaddockMode = parseGHCFlags parseGHCFlags :: DynFlags -> [String] -> IO (DynFlags, [String]) parseGHCFlags dynflags args = case args of [] -> return (dynflags, args) ("-g":rest) -> worker rest ("--ghc-flag":rest) -> worker rest (x:xs) -> do (flags, rest) <- parseGHCFlags dynflags xs return (flags, x:rest) where worker rest = do (mbFlags, rest') <- parseGHCFlag dynflags rest case mbFlags of Just flags -> parseGHCFlags flags rest' Nothing -> parseGHCFlags dynflags rest' parseGHCFlag :: DynFlags -> [String] -> IO (Maybe DynFlags, [String]) parseGHCFlag _ [] = die "No GHC flag supplied\n" parseGHCFlag dynflags args = do mbDyn <- findDynamic case mbDyn of Just (dynflags', rest) -> return (Just dynflags', rest) Nothing -> do mbStat <- findStatic case mbStat of Just (_, rest) -> return (Nothing, rest) Nothing -> die ("Not a GHC flag: " ++ (head args) ++ "\n") where findDynamic = findFlag (\xs -> (do (fs, xs') <- parseDynamicFlags dynflags xs if xs' /= xs then return (Just fs) else return Nothing ) `catch` (\_ -> return Nothing) ) findStatic = findFlag (\xs -> do xs' <- parseStaticFlags xs if xs /= xs' then return (Just ()) else return Nothing ) findFlag p = do xs <- (sequence . snd) (mapAccumL (f p) [] args) case [ (x, index) | Just x <- xs | index <- [1..] ] of ((x, index):_) -> return (Just (x, drop index args)) _ -> return Nothing f :: ([String] -> IO a) -> [String] -> String -> ([String], IO a) f parser previousArgs arg = let args' = previousArgs ++ [arg] in (args', parser args') parseHaddockOpts :: [String] -> IO ([Flag], [String]) parseHaddockOpts words = case getOpt Permute (options True) words of (flags, args, []) -> return (flags, args) (_, _, errors) -> do prog <- getProgramName die (concat errors ++ usageInfo (usageHeader prog) (options False)) usageHeader :: String -> String usageHeader prog = "Usage: " ++ prog ++ " [OPTION...] file...\n" getLibDir :: [String] -> IO (String, [String]) getLibDir ("-B":dir:rest) = return (dir, rest) getLibDir (('-':'B':dir):rest) | not (null dir) = return (dir, rest) getLibDir _ = die "Missing GHC lib dir option: -B