-- | This module defines Superdoc's parameters, as well as the Cabal -- user hooks that do Superdoc's \"real\" work. For an explanation of -- what that work is, see here: "Distribution.Superdoc#HOWITWORKS". -- -- Further, this module exposes some low-level functions that are not -- exposed in "Distribution.Superdoc", but that may be useful for some -- specialized applications. module Distribution.Superdoc.Hooks where import Distribution.Superdoc.Markup import Distribution.Superdoc.UTF8 import Distribution.Simple import Distribution.Simple.LocalBuildInfo import Distribution.Simple.Program.Db (userSpecifyArgs) import Distribution.Simple.Setup import Distribution.PackageDescription (PackageDescription(..)) import Data.List import Control.Exception (throwIO) import Control.Monad (when) import qualified Data.Set as Set import Data.Set (Set) import System.Directory import System.FilePath import System.Info -- ---------------------------------------------------------------------- -- * Superdoc parameters -- | A data structure to hold Superdoc's parameters. Additional -- parameters may be added in the future, so to ensure forward -- compatibility, packages should always specify parameters by -- modifying 'defaultSuperdocArgs', for example like this: -- -- > let params = defaultSuperdocArgs { imageSource = ... } data SuperdocArgs = SuperdocArgs { -- | Where to look for image files. imageSource :: ImageSource, -- | What to do about missing image files. imageMissing :: ImageMissing, -- | Bootstrapping mode. Required to generate Superdoc's own -- documentation, because at that point, Superdoc is typically not -- yet installed. Other packages using Superdoc should not set this -- flag. bootstrap :: Bool } deriving (Show, Eq) -- | The default Superdoc parameters. defaultSuperdocArgs :: SuperdocArgs defaultSuperdocArgs = SuperdocArgs { imageSource = CopyFrom "images", imageMissing = FailOnMissing, bootstrap = False } -- | This parameter specifies where to find image files that are -- linked to in the documentation. data ImageSource = CopyFrom FilePath -- ^ Copy image files from the specified source directory, which -- is relative to the package root. The default is \"@images@\". -- Superdoc will copy the required images to their appropriate -- location within the generated HTML documentation. The image -- files should also be declared as @extra-source-files@ in the -- @.cabal@ file. | ExtraHtmlFiles -- ^ Assume the images are already present in the directory where -- the Haddock documentation is being generated. This is useful if -- the images have been declared as @extra-html-files@ in the -- @.cabal@ file. They will then have been copied to their -- rightful location by Cabal. Superdoc will check that the -- required images are present. deriving (Show, Eq) -- | This parameter specifies what to do when the documentation links -- to an image that does not exist. data ImageMissing = FailOnMissing -- ^ This is the default setting. Cabal will fail with an error -- message if the documentation links to an image that does not -- exist or cannot be found. | IgnoreMissing -- ^ Ignore missing image files. Superdoc will still generate HTML -- image tags, but will not fail if a corresponding image file -- does not exist. This setting may be useful for testing, or to -- build documentation when the image files are incomplete or -- missing. It is not recommended to use this setting for -- production purposes. deriving (Show, Eq) -- ---------------------------------------------------------------------- -- * Hooks for the setup program -- ---------------------------------------------------------------------- -- ** A hook for Haddock -- | A hook for Haddock. This simply adds a command line option to the -- subordinate Haddock process, instructing it to use the -- @superdoc-armor@ preprocessor. Its purpose is to replace -- UTF8-encoded Unicode characters by their ASCII armor. haddockhook :: SuperdocArgs -> PackageDescription -> LocalBuildInfo -> UserHooks -> HaddockFlags -> IO () haddockhook sdargs desc info hooks flags = do -- Add preprocessor arguments to haddock let progs = withPrograms info let progs' = userSpecifyArgs "haddock" hargs progs let info' = info { withPrograms = progs' } let args = haddockProgramArgs flags let args' = args ++ [("haddock", hargs)] let flags' = flags { haddockProgramArgs = args' } -- Run the default handler, with the modified arguments haddockHook simpleUserHooks desc info' hooks flags' where hargs | bootstrap sdargs == False = ["--optghc=-F", "--optghc=-pgmF", "--optghc=superdoc-armor"] | os == "mingw32" = ["--optghc=-F", "--optghc=-pgmF", "--optghc=scripts/preproc.bat"] | otherwise = ["--optghc=-F", "--optghc=-pgmF", "--optghc=scripts/preproc.sh"] -- ---------------------------------------------------------------------- -- ** A post-Haddock hook -- | A hook to run after Haddock completes. This resolves Superdoc -- markup and ASCII armor in all generated HTML files. It also copies -- all of the linked images to the HTML directory (depending on the -- 'imageSource' and 'imageMissing' parameters). Finally, it checks -- for the \"@--hyperlink-source@\" option, and if necessary, calls -- the 'posthscolour' hook as well (because it is not run from inside -- Cabal in this situation). -- -- The first argument hold the Superdoc parameters, and the remaining -- arguments are as for any 'postHaddock' hook. posthaddock :: SuperdocArgs -> Args -> HaddockFlags -> PackageDescription -> LocalBuildInfo -> IO () posthaddock sdargs args flags desc info = do -- Find documentation's build directory let package_name = unPackageName (pkgName (package desc)) let distdir = case haddockDistPref flags of Flag distdir -> distdir NoFlag -> buildDir info ".." let docdir = distdir "doc" "html" package_name -- Although the --hyperlink-source option causes HsColour to be run, -- it fails to run the postHscolour hook. So we run it explicitly. when (haddockHscolour flags == Flag True) $ do do_posthscolour docdir -- Now do the actual work. do_posthaddock sdargs docdir -- | Do the actual work of the 'posthaddock' hook, given a path to the -- documentation directory. do_posthaddock :: SuperdocArgs -> FilePath -> IO () do_posthaddock sdargs docdir = do -- Make a list of HTML files. files <- getDirectoryContents docdir let htmlfiles = [ docdir f | f <- files, ".html" `isSuffixOf` f ] -- Process them. imagelist <- filter_files markup htmlfiles let images = Set.unions imagelist handle_images (imageSource sdargs) (imageMissing sdargs) images docdir -- | Perform the action described by 'ImageSource' and 'ImageMissing', -- for the given set of images. The last argument is the path for the -- documentation directory. handle_images :: ImageSource -> ImageMissing -> Set FilePath -> FilePath -> IO () handle_images ExtraHtmlFiles IgnoreMissing images docdir = return () handle_images ExtraHtmlFiles FailOnMissing images docdir = do sequence_ $ do f <- Set.toList images return $ do exists <- doesFileExist (docdir f) when (not exists) $ do throwIO (userError (f ++ ": does not exist")) handle_images (CopyFrom imagedir) flag images docdir = do -- Copy the required image files to the HTML directory. If flag == -- FailOnMissing, raise an IO error if some image is not present. sequence_ $ do f <- Set.toList images return $ do exists_src <- doesFileExist (imagedir f) if exists_src then do copyFile (imagedir f) (docdir f) else if flag == IgnoreMissing then do return () else throwIO (userError (f ++ ": does not exist")) -- ---------------------------------------------------------------------- -- ** A post-HsColour hook -- | A hook to run after HsColour completes. This goes through the -- files generated by HsColour and replaces UTF8 characters by their -- corresponding HTML escapes. posthscolour :: SuperdocArgs -> Args -> HscolourFlags -> PackageDescription -> LocalBuildInfo -> IO () posthscolour sdargs args flags desc info = do -- Find documentation's build directory let package_name = unPackageName (pkgName (package desc)) let distdir = case hscolourDistPref flags of Flag distdir -> distdir NoFlag -> buildDir info ".." let docdir = distdir "doc" "html" package_name do_posthscolour docdir -- | Do the actual work of the 'posthaddock' hook, given a path to the -- documentation directory. do_posthscolour :: FilePath -> IO () do_posthscolour docdir = do let hscolourdir = docdir "src" -- Make a list of HTML files. files <- getDirectoryContents hscolourdir let htmlfiles = [ hscolourdir f | f <- files, ".html" `isSuffixOf` f ] -- Process them. filter_files (filter_id . to_html . parse_utf8) htmlfiles return ()