    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Distribution.Simple.Program.Hpc
    -- Copyright   :  Thomas Tuegel 2011
    --
    -- Maintainer  :  cabal-devel@haskell.org
    -- Portability :  portable
    --
    -- This module provides an library interface to the @hpc@ program.

    module Distribution.Simple.Program.Hpc
        ( markup
        , union
        ) where

    import Distribution.ModuleName ( ModuleName )
    import Distribution.Simple.Program.Run
             ( ProgramInvocation, programInvocation, runProgramInvocation )
    import Distribution.Simple.Program.Types ( ConfiguredProgram(..) )
    import Distribution.Text ( display )
    import Distribution.Simple.Utils ( warn )
    import Distribution.Verbosity ( Verbosity )
    import Distribution.Version ( Version(..), orLaterVersion, withinRange )

    -- | Invoke hpc with the given parameters.
    --
    -- Prior to HPC version 0.7 (packaged with GHC 7.8), hpc did not handle
    -- multiple .mix paths correctly, so we print a warning, and only pass it the
    -- first path in the list. This means that e.g. test suites that import their
    -- library as a dependency can still work, but those that include the library
    -- modules directly (in other-modules) don't.
    markup :: ConfiguredProgram
           -> Version
           -> Verbosity
           -> FilePath            -- ^ Path to .tix file
           -> [FilePath]          -- ^ Paths to .mix file directories
           -> FilePath            -- ^ Path where html output should be located
           -> [ModuleName]        -- ^ List of modules to exclude from report
           -> IO ()
    markup hpc hpcVer verbosity tixFile hpcDirs destDir excluded = do
        hpcDirs' <- if withinRange hpcVer (orLaterVersion version07)
            then return hpcDirs
            else do
                warn verbosity $ "Your version of HPC (" ++ display hpcVer
                    ++ ") does not properly handle multiple search paths. "
                    ++ "Coverage report generation may fail unexpectedly. These "
                    ++ "issues are addressed in version 0.7 or later (GHC 7.8 or "
                    ++ "later)."
                    ++ if null droppedDirs
                        then ""
                        else " The following search paths have been abandoned: "
                            ++ show droppedDirs
                return passedDirs

        runProgramInvocation verbosity
          (markupInvocation hpc tixFile hpcDirs' destDir excluded)
      where
        version07 = Version [0, 7] []
        (passedDirs, droppedDirs) = splitAt 1 hpcDirs

    markupInvocation :: ConfiguredProgram
                     -> FilePath            -- ^ Path to .tix file
                     -> [FilePath]          -- ^ Paths to .mix file directories
                     -> FilePath            -- ^ Path where html output should be
                                            -- located
                     -> [ModuleName]        -- ^ List of modules to exclude from
                                            -- report
                     -> ProgramInvocation
    markupInvocation hpc tixFile hpcDirs destDir excluded =
        let args = [ "markup", tixFile
                   , "--destdir=" ++ destDir
                   ]
                ++ map ("--hpcdir=" ++) hpcDirs
                ++ ["--exclude=" ++ display moduleName
                   | moduleName <- excluded ]
        in programInvocation hpc args

    union :: ConfiguredProgram
          -> Verbosity
          -> [FilePath]         -- ^ Paths to .tix files
          -> FilePath           -- ^ Path to resultant .tix file
          -> [ModuleName]       -- ^ List of modules to exclude from union
          -> IO ()
    union hpc verbosity tixFiles outFile excluded =
        runProgramInvocation verbosity
          (unionInvocation hpc tixFiles outFile excluded)

    unionInvocation :: ConfiguredProgram
                    -> [FilePath]       -- ^ Paths to .tix files
                    -> FilePath         -- ^ Path to resultant .tix file
                    -> [ModuleName]     -- ^ List of modules to exclude from union
                    -> ProgramInvocation
    unionInvocation hpc tixFiles outFile excluded =
        programInvocation hpc $ concat
            [ ["sum", "--union"]
            , tixFiles
            , ["--output=" ++ outFile]
            , ["--exclude=" ++ display moduleName
              | moduleName <- excluded ]
            ]