Skip to content

Commit 408ff44

Browse files
committed
HPC artifacts are written and read from pkg-db
This commit re-designs the mechanism by which we make the .mix files of libraries available to produce the Haskell Program Coverage report after running testsuites. The idea, for the Cabal library, is: * Cabal builds libraries with -fhpc, and store the hpc artifacts in build </> `extraCompilationArtifacts` * At Cabal install time, `extraCompilationArtifacts` is copied into the package database * At Cabal configure time, we both - receive as --coverage-for flags unit-ids of library components from the same package (ultimately, when haskell#9493 is resolved, we will receive unit ids of libraries in other packages in the same project too), - and, when configuring a whole package instead of just a testsuite component, we determine the unit-ids of libraries in the package these unit-ids are written into `configCoverageFor` in `ConfigFlags` * At Cabal test time, for each library to cover (stored in `configCoverageFor`), we look in the package database for the hpc dirs, which we eventually pass along to the `hpc markup` call as `--hpcdir` flags As for cabal-install: * After a plan has been elaborated, we select the packages which can be covered and pass them to Cabal's ./Setup configure as --coverage-for=<unit-id> flags. - Notably, valid libraries are non-indefinite and non-instantiations, since HPC does not support backpack. - Furthermore, we only include libraries in the same package as the component being configured, despite possibly there being more library components in other packages of the same project. When haskell#9493 is resolved, we could lift this restriction and pass all libraries local to the package as --coverage-for. See `determineCoverageFor` and `shouldCoverPkg` in Distribution.Client.ProjectPlanning. Fixes haskell#6440 (internal libs coverage), haskell#6397 (backpack breaks coverage), doesn't yet fix haskell#8609 (multi-package coverage report) which is tracked in haskell#9493, and fixes in a new way the previously fixed haskell#4798, haskell#5213.
1 parent 116de5e commit 408ff44

File tree

24 files changed

+213
-142
lines changed

24 files changed

+213
-142
lines changed

Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ md5CheckGenericPackageDescription proxy = md5Check proxy
4141
md5CheckLocalBuildInfo :: Proxy LocalBuildInfo -> Assertion
4242
md5CheckLocalBuildInfo proxy = md5Check proxy
4343
#if MIN_VERSION_base(4,19,0)
44-
0x205fbe2649bc5e488bce50c07a71cadb
44+
0x512e880894570552f08aa82547568dbc
4545
#else
46-
0x26e91a71ebd19d4d6ce37f798ede249a
46+
0x968807984ad42d41a9e9ab696a9fec58
4747
#endif

Cabal/src/Distribution/Simple/Configure.hs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
{-# LANGUAGE DeriveDataTypeable #-}
22
{-# LANGUAGE FlexibleContexts #-}
3+
{-# LANGUAGE LambdaCase #-}
4+
{-# LANGUAGE NamedFieldPuns #-}
35
{-# LANGUAGE OverloadedStrings #-}
46
{-# LANGUAGE RankNTypes #-}
57
{-# LANGUAGE RecordWildCards #-}
@@ -44,6 +46,7 @@ module Distribution.Simple.Configure
4446
, localBuildInfoFile
4547
, getInstalledPackages
4648
, getInstalledPackagesMonitorFiles
49+
, getInstalledPackageById
4750
, getPackageDBContents
4851
, configCompilerEx
4952
, configCompilerAuxEx
@@ -78,7 +81,7 @@ import Distribution.Simple.BuildTarget
7881
import Distribution.Simple.BuildToolDepends
7982
import Distribution.Simple.Compiler
8083
import Distribution.Simple.LocalBuildInfo
81-
import Distribution.Simple.PackageIndex (InstalledPackageIndex)
84+
import Distribution.Simple.PackageIndex (InstalledPackageIndex, lookupUnitId)
8285
import qualified Distribution.Simple.PackageIndex as PackageIndex
8386
import Distribution.Simple.PreProcess
8487
import Distribution.Simple.Program
@@ -162,6 +165,7 @@ import qualified Data.Maybe as M
162165
import qualified Data.Set as Set
163166
import qualified Distribution.Compat.NonEmptySet as NES
164167
import Distribution.Simple.Errors
168+
import Distribution.Simple.Flag (mergeListFlag)
165169
import Distribution.Types.AnnotatedId
166170

167171
type UseExternalInternalDeps = Bool
@@ -877,10 +881,21 @@ configure (pkg_descr0, pbi) cfg = do
877881
Map.empty
878882
buildComponents
879883

884+
-- For whole-package configure, we have to determine the additional
885+
-- configCoverageFor of the main lib and sub libs here.
886+
let extraCoverageFor :: [UnitId] = case enabled of
887+
-- Whole package configure, add package libs
888+
ComponentRequestedSpec{} -> mapMaybe (\case LibComponentLocalBuildInfo{componentUnitId} -> Just componentUnitId; _ -> Nothing) buildComponents
889+
-- Component configure, no need to do anything
890+
OneComponentRequestedSpec{} -> []
891+
892+
-- TODO: Should we also enforce something here on that --coverage-for cannot
893+
-- include indefinite components or instantiations?
894+
880895
let lbi =
881896
(setCoverageLBI . setProfLBI)
882897
LocalBuildInfo
883-
{ configFlags = cfg
898+
{ configFlags = cfg{configCoverageFor = mergeListFlag (configCoverageFor cfg) (toFlag extraCoverageFor)}
884899
, flagAssignment = flags
885900
, componentEnabledSpec = enabled
886901
, extraConfigArgs = [] -- Currently configure does not
@@ -1747,6 +1762,13 @@ getInstalledPackagesMonitorFiles verbosity comp packageDBs progdb platform =
17471762
++ prettyShow other
17481763
return []
17491764

1765+
-- | Looks up the 'InstalledPackageInfo' of a given 'UnitId' from the
1766+
-- 'PackageDBStack' in the 'LocalBuildInfo'.
1767+
getInstalledPackageById :: Verbosity -> LocalBuildInfo -> UnitId -> IO (Maybe InstalledPackageInfo)
1768+
getInstalledPackageById verbosity LocalBuildInfo{compiler, withPackageDB, withPrograms} unitid = do
1769+
ipindex <- getInstalledPackages verbosity compiler withPackageDB withPrograms
1770+
return $ lookupUnitId ipindex unitid
1771+
17501772
-- | The user interface specifies the package dbs to use with a combination of
17511773
-- @--global@, @--user@ and @--package-db=global|user|clear|$file@.
17521774
-- This function combines the global/user flag and interprets the package-db

Cabal/src/Distribution/Simple/GHC/BuildGeneric.hs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import Distribution.PackageDescription.Utils (cabalBug)
1919
import Distribution.Pretty
2020
import Distribution.Simple.BuildPaths
2121
import Distribution.Simple.Compiler
22-
import Distribution.Simple.Flag (Flag (..), fromFlag, toFlag)
2322
import Distribution.Simple.GHC.Build
2423
( checkNeedsRecompilation
2524
, componentGhcOptions
@@ -39,7 +38,7 @@ import Distribution.Simple.LocalBuildInfo
3938
import qualified Distribution.Simple.PackageIndex as PackageIndex
4039
import Distribution.Simple.Program
4140
import Distribution.Simple.Program.GHC
42-
import Distribution.Simple.Setup.Config
41+
import Distribution.Simple.Setup.Common
4342
import Distribution.Simple.Setup.Repl
4443
import Distribution.Simple.Utils
4544
import Distribution.System
@@ -399,10 +398,9 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do
399398
-- Determine if program coverage should be enabled and if so, what
400399
-- '-hpcdir' should be.
401400
let isCoverageEnabled = exeCoverage lbi
402-
distPref = fromFlag $ configDistPref $ configFlags lbi
403401
hpcdir way
404402
| gbuildIsRepl bm = mempty -- HPC is not supported in ghci
405-
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way
403+
| isCoverageEnabled = toFlag $ Hpc.mixDir (tmpDir </> extraCompilationArtifacts) way
406404
| otherwise = mempty
407405

408406
rpaths <- getRPaths lbi clbi

Cabal/src/Distribution/Simple/GHC/BuildOrRepl.hs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import Distribution.Package
99
import Distribution.PackageDescription as PD
1010
import Distribution.Simple.BuildPaths
1111
import Distribution.Simple.Compiler
12-
import Distribution.Simple.Flag (Flag (..), fromFlag, toFlag)
1312
import Distribution.Simple.GHC.Build
1413
( checkNeedsRecompilation
1514
, componentGhcOptions
@@ -27,7 +26,7 @@ import Distribution.Simple.Program
2726
import qualified Distribution.Simple.Program.Ar as Ar
2827
import Distribution.Simple.Program.GHC
2928
import qualified Distribution.Simple.Program.Ld as Ld
30-
import Distribution.Simple.Setup.Config
29+
import Distribution.Simple.Setup.Common
3130
import Distribution.Simple.Setup.Repl
3231
import Distribution.Simple.Utils
3332
import Distribution.System
@@ -96,10 +95,9 @@ buildOrReplLib mReplFlags verbosity numJobs pkg_descr lbi lib clbi = do
9695
-- Determine if program coverage should be enabled and if so, what
9796
-- '-hpcdir' should be.
9897
let isCoverageEnabled = libCoverage lbi
99-
distPref = fromFlag $ configDistPref $ configFlags lbi
10098
hpcdir way
10199
| forRepl = mempty -- HPC is not supported in ghci
102-
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way
100+
| isCoverageEnabled = toFlag $ Hpc.mixDir (libTargetDir </> extraCompilationArtifacts) way
103101
| otherwise = mempty
104102

105103
createDirectoryIfMissingVerbose verbosity True libTargetDir

Cabal/src/Distribution/Simple/GHCJS.hs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ import Distribution.Simple.Program
7272
import Distribution.Simple.Program.GHC
7373
import qualified Distribution.Simple.Program.HcPkg as HcPkg
7474
import qualified Distribution.Simple.Program.Strip as Strip
75-
import Distribution.Simple.Setup.Config
75+
import Distribution.Simple.Setup.Common
7676
import Distribution.Simple.Utils
7777
import Distribution.System
7878
import Distribution.Types.ComponentLocalBuildInfo
@@ -515,10 +515,9 @@ buildOrReplLib mReplFlags verbosity numJobs _pkg_descr lbi lib clbi = do
515515
-- Determine if program coverage should be enabled and if so, what
516516
-- '-hpcdir' should be.
517517
let isCoverageEnabled = libCoverage lbi
518-
distPref = fromFlag $ configDistPref $ configFlags lbi
519518
hpcdir way
520519
| forRepl = mempty -- HPC is not supported in ghci
521-
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way
520+
| isCoverageEnabled = toFlag $ Hpc.mixDir (libTargetDir </> extraCompilationArtifacts) way
522521
| otherwise = mempty
523522

524523
createDirectoryIfMissingVerbose verbosity True libTargetDir
@@ -1235,10 +1234,9 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do
12351234
-- Determine if program coverage should be enabled and if so, what
12361235
-- '-hpcdir' should be.
12371236
let isCoverageEnabled = exeCoverage lbi
1238-
distPref = fromFlag $ configDistPref $ configFlags lbi
12391237
hpcdir way
12401238
| gbuildIsRepl bm = mempty -- HPC is not supported in ghci
1241-
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way
1239+
| isCoverageEnabled = toFlag $ Hpc.mixDir (tmpDir </> extraCompilationArtifacts) way
12421240
| otherwise = mempty
12431241

12441242
rpaths <- getRPaths lbi clbi

Cabal/src/Distribution/Simple/Hpc.hs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,19 @@ module Distribution.Simple.Hpc
2828
import Distribution.Compat.Prelude
2929
import Prelude ()
3030

31-
import Distribution.ModuleName (main)
31+
import Distribution.ModuleName (ModuleName, main)
3232
import Distribution.PackageDescription
3333
( TestSuite (..)
3434
, testModules
3535
)
3636
import qualified Distribution.PackageDescription as PD
3737
import Distribution.Pretty
38-
import Distribution.Simple.Flag (fromFlagOrDefault)
3938
import Distribution.Simple.LocalBuildInfo (LocalBuildInfo (..))
4039
import Distribution.Simple.Program
4140
( hpcProgram
4241
, requireProgramVersion
4342
)
4443
import Distribution.Simple.Program.Hpc (markup, union)
45-
import Distribution.Simple.Setup (TestFlags (..))
4644
import Distribution.Simple.Utils (notice)
4745
import Distribution.Types.UnqualComponentName
4846
import Distribution.Verbosity (Verbosity ())
@@ -115,14 +113,15 @@ guessWay lbi
115113
-- | Generate the HTML markup for a package's test suites.
116114
markupPackage
117115
:: Verbosity
118-
-> TestFlags
116+
-> [FilePath]
117+
-> [ModuleName]
119118
-> LocalBuildInfo
120119
-> FilePath
121120
-- ^ Testsuite \"dist/\" prefix
122121
-> PD.PackageDescription
123122
-> [TestSuite]
124123
-> IO ()
125-
markupPackage verbosity TestFlags{testCoverageDistPrefs, testCoverageLibsModules} lbi testDistPref pkg_descr suites = do
124+
markupPackage verbosity testCoverageDistPrefs testCoverageLibsModules lbi testDistPref pkg_descr suites = do
126125
let tixFiles = map (tixFilePath testDistPref way) testNames
127126
tixFilesExist <- traverse doesFileExist tixFiles
128127
when (and tixFilesExist) $ do
@@ -168,5 +167,5 @@ markupPackage verbosity TestFlags{testCoverageDistPrefs, testCoverageLibsModules
168167
where
169168
way = guessWay lbi
170169
testNames = fmap (unUnqualComponentName . testName) suites
171-
mixDirs = map (`mixDir` way) (fromFlagOrDefault [] testCoverageDistPrefs)
172-
included = fromFlagOrDefault [] testCoverageLibsModules
170+
mixDirs = map (`mixDir` way) (testCoverageDistPrefs)
171+
included = testCoverageLibsModules

Cabal/src/Distribution/Simple/Setup/Config.hs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import Distribution.Types.DumpBuildInfo
5454
import Distribution.Types.GivenComponent
5555
import Distribution.Types.Module
5656
import Distribution.Types.PackageVersionConstraint
57+
import Distribution.Types.UnitId
5758
import Distribution.Utils.NubList
5859
import Distribution.Verbosity
5960
import qualified Text.PrettyPrint as Disp
@@ -220,6 +221,11 @@ data ConfigFlags = ConfigFlags
220221
-- ^ Allow depending on private sublibraries. This is used by external
221222
-- tools (like cabal-install) so they can add multiple-public-libraries
222223
-- compatibility to older ghcs by checking visibility externally.
224+
, configCoverageFor :: Flag [UnitId]
225+
-- ^ The list of libraries to be included in the hpc coverage report for
226+
-- testsuites run with @--enable-coverage@. Notably, this list must exclude
227+
-- indefinite libraries and instantiations because HPC does not support
228+
-- backpack (Nov. 2023).
223229
}
224230
deriving (Generic, Read, Show, Typeable)
225231

@@ -288,6 +294,7 @@ instance Eq ConfigFlags where
288294
&& equal configDebugInfo
289295
&& equal configDumpBuildInfo
290296
&& equal configUseResponseFiles
297+
&& equal configCoverageFor
291298
where
292299
equal f = on (==) f a b
293300

@@ -828,6 +835,22 @@ configureOptions showOrParseArgs =
828835
configAllowDependingOnPrivateLibs
829836
(\v flags -> flags{configAllowDependingOnPrivateLibs = v})
830837
trueArg
838+
, option
839+
""
840+
["coverage-for"]
841+
"A list of unit-ids of libraries to include in the Haskell Program Coverage report."
842+
configCoverageFor
843+
( \v flags ->
844+
flags
845+
{ configCoverageFor =
846+
mergeListFlag (configCoverageFor flags) v
847+
}
848+
)
849+
( reqArg'
850+
"UNITID"
851+
(Flag . (: []) . fromString)
852+
(fmap prettyShow . fromFlagOrDefault [])
853+
)
831854
]
832855
where
833856
liftInstallDirs =

Cabal/src/Distribution/Simple/Setup/Test.hs

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ import Distribution.Simple.Utils
4040
import Distribution.Verbosity
4141
import qualified Text.PrettyPrint as Disp
4242

43-
import Distribution.ModuleName (ModuleName)
4443
import Distribution.Simple.Setup.Common
4544

4645
-- ------------------------------------------------------------
@@ -89,15 +88,6 @@ data TestFlags = TestFlags
8988
, testKeepTix :: Flag Bool
9089
, testWrapper :: Flag FilePath
9190
, testFailWhenNoTestSuites :: Flag Bool
92-
, testCoverageLibsModules :: Flag [ModuleName]
93-
-- ^ The list of all modules from libraries in the local project that should
94-
-- be included in the hpc coverage report.
95-
, testCoverageDistPrefs :: Flag [FilePath]
96-
-- ^ The path to each library local to this project and to the test
97-
-- components being built, to include in coverage reporting (notably, this
98-
-- excludes indefinite libraries and instantiations because HPC does not
99-
-- support backpack - Nov. 2023). Cabal uses these paths as dist prefixes to
100-
-- determine the path to the `mix` dirs of each component to cover.
10191
, -- TODO: think about if/how options are passed to test exes
10292
testOptions :: [PathTemplate]
10393
}
@@ -114,8 +104,6 @@ defaultTestFlags =
114104
, testKeepTix = toFlag False
115105
, testWrapper = NoFlag
116106
, testFailWhenNoTestSuites = toFlag False
117-
, testCoverageLibsModules = NoFlag
118-
, testCoverageDistPrefs = NoFlag
119107
, testOptions = []
120108
}
121109

@@ -221,38 +209,6 @@ testOptions' showOrParseArgs =
221209
testFailWhenNoTestSuites
222210
(\v flags -> flags{testFailWhenNoTestSuites = v})
223211
trueArg
224-
, option
225-
[]
226-
["coverage-module"]
227-
"Module of a project-local library to include in the HPC report"
228-
testCoverageLibsModules
229-
( \v flags ->
230-
flags
231-
{ testCoverageLibsModules =
232-
mergeListFlag (testCoverageLibsModules flags) v
233-
}
234-
)
235-
( reqArg'
236-
"MODULE"
237-
(Flag . (: []) . fromString)
238-
(fmap prettyShow . fromFlagOrDefault [])
239-
)
240-
, option
241-
[]
242-
["coverage-dist-dir"]
243-
"The directory where Cabal puts generated build files of an HPC enabled component"
244-
testCoverageDistPrefs
245-
( \v flags ->
246-
flags
247-
{ testCoverageDistPrefs =
248-
mergeListFlag (testCoverageDistPrefs flags) v
249-
}
250-
)
251-
( reqArg'
252-
"DIR"
253-
(Flag . (: []))
254-
(fromFlagOrDefault [])
255-
)
256212
, option
257213
[]
258214
["test-options"]

0 commit comments

Comments
 (0)