{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} ----------------------------------------------------------------------------- -- | -- Module : Distribution.Simple.Hpc -- Copyright : Thomas Tuegel 2011 -- License : BSD3 -- -- Maintainer : cabal-devel@haskell.org -- Portability : portable -- -- This module provides functions for locating various HPC-related paths and -- a function for adding the necessary options to a PackageDescription to -- build test suites with HPC enabled. module Distribution.Simple.Hpc ( Way(..), guessWay , htmlDir , mixDir , tixDir , tixFilePath , markupPackage , markupTest ) where import Prelude () import Distribution.Compat.Prelude import Distribution.Types.UnqualComponentName import Distribution.ModuleName ( main ) import qualified Distribution.PackageDescription as PD import Distribution.PackageDescription ( Library(..) , TestSuite(..) , testModules ) import Distribution.Pretty import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo(..) ) import Distribution.Simple.Program ( hpcProgram , requireProgramVersion ) import Distribution.Simple.Program.Hpc ( markup, union ) import Distribution.Simple.Utils ( notice ) import Distribution.Version ( anyVersion ) import Distribution.Verbosity ( Verbosity() ) import System.Directory ( createDirectoryIfMissing, doesFileExist ) import System.FilePath -- ------------------------------------------------------------------------- -- Haskell Program Coverage data Way = Vanilla | Prof | Dyn deriving (Bounded, Enum, Eq, Read, Show) hpcDir :: FilePath -- ^ \"dist/\" prefix -> Way -> FilePath -- ^ Directory containing component's HPC .mix files hpcDir distPref way = distPref "hpc" wayDir where wayDir = case way of Vanilla -> "vanilla" Prof -> "prof" Dyn -> "dyn" mixDir :: FilePath -- ^ \"dist/\" prefix -> Way -> FilePath -- ^ Component name -> FilePath -- ^ Directory containing test suite's .mix files mixDir distPref way name = hpcDir distPrefBuild way "mix" name where -- This is a hack for HPC over test suites, needed to match the directory -- where HPC saves and reads .mix files when the main library of the same -- package is being processed, perhaps in a previous cabal run (#5213). -- E.g., @distPref@ may be -- @./dist-newstyle/build/x86_64-linux/ghc-9.0.1/cabal-gh5213-0.1/t/tests@ -- but the path where library mix files reside has two less components -- at the end (@t/tests@) and this reduced path needs to be passed to -- both @hpc@ and @ghc@. For non-default optimization levels, the path -- suffix is one element longer and the extra path element needs -- to be preserved. distPrefElements = splitDirectories distPref distPrefBuild = case drop (length distPrefElements - 3) distPrefElements of ["t", _, "noopt"] -> joinPath $ take (length distPrefElements - 3) distPrefElements ++ ["noopt"] ["t", _, "opt"] -> joinPath $ take (length distPrefElements - 3) distPrefElements ++ ["opt"] [_, "t", _] -> joinPath $ take (length distPrefElements - 2) distPrefElements _ -> distPref tixDir :: FilePath -- ^ \"dist/\" prefix -> Way -> FilePath -- ^ Component name -> FilePath -- ^ Directory containing test suite's .tix files tixDir distPref way name = hpcDir distPref way "tix" name -- | Path to the .tix file containing a test suite's sum statistics. tixFilePath :: FilePath -- ^ \"dist/\" prefix -> Way -> FilePath -- ^ Component name -> FilePath -- ^ Path to test suite's .tix file tixFilePath distPref way name = tixDir distPref way name name <.> "tix" htmlDir :: FilePath -- ^ \"dist/\" prefix -> Way -> FilePath -- ^ Component name -> FilePath -- ^ Path to test suite's HTML markup directory htmlDir distPref way name = hpcDir distPref way "html" name -- | Attempt to guess the way the test suites in this package were compiled -- and linked with the library so the correct module interfaces are found. guessWay :: LocalBuildInfo -> Way guessWay lbi | withProfExe lbi = Prof | withDynExe lbi = Dyn | otherwise = Vanilla -- | Generate the HTML markup for a test suite. markupTest :: Verbosity -> LocalBuildInfo -> FilePath -- ^ \"dist/\" prefix -> String -- ^ Library name -> TestSuite -> Library -> IO () markupTest verbosity lbi distPref libraryName suite library = do tixFileExists <- doesFileExist $ tixFilePath distPref way $ testName' when tixFileExists $ do -- behaviour of 'markup' depends on version, so we need *a* version -- but no particular one (hpc, hpcVer, _) <- requireProgramVersion verbosity hpcProgram anyVersion (withPrograms lbi) let htmlDir_ = htmlDir distPref way testName' markup hpc hpcVer verbosity (tixFilePath distPref way testName') mixDirs htmlDir_ (exposedModules library) notice verbosity $ "Test coverage report written to " ++ htmlDir_ "hpc_index" <.> "html" where way = guessWay lbi testName' = unUnqualComponentName $ testName suite mixDirs = map (mixDir distPref way) [ testName', libraryName ] -- | Generate the HTML markup for all of a package's test suites. markupPackage :: Verbosity -> LocalBuildInfo -> FilePath -- ^ \"dist/\" prefix -> PD.PackageDescription -> [TestSuite] -> IO () markupPackage verbosity lbi distPref pkg_descr suites = do let tixFiles = map (tixFilePath distPref way) testNames tixFilesExist <- traverse doesFileExist tixFiles when (and tixFilesExist) $ do -- behaviour of 'markup' depends on version, so we need *a* version -- but no particular one (hpc, hpcVer, _) <- requireProgramVersion verbosity hpcProgram anyVersion (withPrograms lbi) let outFile = tixFilePath distPref way libraryName htmlDir' = htmlDir distPref way libraryName excluded = concatMap testModules suites ++ [ main ] createDirectoryIfMissing True $ takeDirectory outFile union hpc verbosity tixFiles outFile excluded markup hpc hpcVer verbosity outFile mixDirs htmlDir' included notice verbosity $ "Package coverage report written to " ++ htmlDir' "hpc_index.html" where way = guessWay lbi testNames = fmap (unUnqualComponentName . testName) suites mixDirs = map (mixDir distPref way) $ libraryName : testNames included = concatMap (exposedModules) $ PD.allLibraries pkg_descr libraryName = prettyShow $ PD.package pkg_descr