brandonchinn178 / hpc-lcov

Convert HPC coverage output to LCOV format
http://hackage.haskell.org/package/hpc-lcov
BSD 3-Clause "New" or "Revised" License
6 stars 2 forks source link

Support Cabal #3

Open brandon-leapyear opened 4 years ago

brandon-leapyear commented 4 years ago

Currently, hpc-lcov is hardcoded to look at Stack paths (e.g. readStack). Enable looking at Cabal paths as well

guibou commented 6 months ago

I used that in an old codebase:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

-- This file wraps hpc-lcov to something which understand the bazel layout

import Data.List (isPrefixOf)
import qualified Data.Map as Map
import Data.Traversable (for)
import qualified Options.Applicative as Opt
import Path (Dir, File, Path)
import qualified Path
import Path.IO (resolveFile')
import Trace.Hpc.Lcov (generateLcovFromTix, writeReport)
import Trace.Hpc.Mix (Mix (..), readMix)
import Trace.Hpc.Tix (Tix (..), TixModule, readTix, tixModuleName)

data CLIOptions = CLIOptions
  { cliTixFiles :: [FilePath],
    cliOutput :: FilePath,
    mixDirectories :: [FilePath]
  }

getCLIOptions :: IO CLIOptions
getCLIOptions =
  Opt.execParser $
    Opt.info (Opt.helper <*> parseCLIOptions) $ Opt.progDesc description
  where
    parseCLIOptions =
      CLIOptions
        <$> parseCLITixFiles
        <*> parseCLIOutput
        <*> parseMixDirectories
    parseCLITixFiles =
      Opt.many $
        Opt.strArgument $
          mconcat
            [ Opt.metavar "TIX_FILE",
              Opt.help ".tix file(s) to convert"
            ]
    parseMixDirectories =
      Opt.some $
        Opt.strOption $
          mconcat
            [ Opt.long "mix",
              Opt.short 'm',
              Opt.metavar "MIX_DIRECTORY",
              Opt.help ".mix directories"
            ]
    parseCLIOutput =
      Opt.strOption $
        mconcat
          [ Opt.long "output",
            Opt.short 'o',
            Opt.metavar "FILE",
            Opt.help "The file to save coverage information (default: lcov.info)",
            Opt.value "lcov.info"
          ]

    description = "Convert HPC coverage output into the LCOV format"

main :: IO ()
main = do
  CLIOptions {..} <- getCLIOptions

  tixFiles <- traverse resolveFile' cliTixFiles

  tixModules <- concat <$> traverse readTixPath tixFiles

  mixDirectories' <- traverse Path.parseAbsDir mixDirectories

  moduleToMixList <- for tixModules $ \tixModule -> do
    Mix fileLoc _ _ _ mixEntries <- readMixPath mixDirectories' (Right tixModule)
    fileLocRelPath <- Path.parseRelFile fileLoc

    let modulePath = fileLocRelPath

    pure (tixModuleName tixModule, (Path.toFilePath modulePath, mixEntries))

  let moduleToMix = Map.toList . Map.fromListWith checkDupeMix $ moduleToMixList
      checkDupeMix mix1 mix2 =
        if mix1 == mix2
          then mix1
          else error $ ".mix files differ: " <> show (mix1, mix2)
      report = generateLcovFromTix moduleToMix tixModules

  writeReport cliOutput report

{- HPC file readers -}

readTixPath :: Path b File -> IO [TixModule]
readTixPath path = do
  Tix tixModules <-
    readTix (Path.toFilePath path)
      >>= maybe (error $ "Could not find tix file: " <> Path.toFilePath path) pure
  pure tixModules

readMixPath :: [Path b Dir] -> Either String TixModule -> IO Mix
readMixPath = readMix . fmap Path.toFilePath

Comes with no guarantee and no more details for now, but it was able to convert .tix/mix files from arbitrary layout (from bazel, but could work with cabal too) to an lcov file then sent to codecov.