Skip to content

Commit 98c71b9

Browse files
committed
Allow per-component builds with coverage enabled
This commits re-enables per-component builds when coverage checking is enabled. This restriction was previously added in haskell#5004 to fix haskell#4798. However, the fix for haskell#5213, in haskell#7493, fixes the paths of the testsuite `.mix` files to the same location as that of the main library component, which in turn fixes haskell#4798 as well -- meaning the restriction to treat testsuites per-package (legacy-fallback) is no longer needed. 1. We allow hpc in per-component builds 2. To generate hpc files in the appropriate component directories in the distribution tree, we move the hack from haskell#7493 from dictating the `.mix` directories where hpc information is stored to dictating the `.mix` directories that are included in the call to `hpc markup`. We also drop an unnecessary directory in the hpc file hierarchy. 3. To account for internal libraries, we include the mix dirs and exposed modules of all (non-indefinite) libraries in the package 4. We only add non-indefinite libraries to the hpc markup command. Indefinite libraries and instantiations are ignored as it is not obvious what it means for HPC to support backpack, e.g. covering a library function that two different instantiations The combination of (1,3) fix haskell#6440, and adding (4) fixes haskell#6397 Includes regression tests for haskell#6440, haskell#6397, and haskell#4798 (the test for haskell#5213 already exists) Fixes haskell#6440, haskell#6397, and fixes in a new way the previously fixed haskell#4798, haskell#5213.
1 parent 7b4750a commit 98c71b9

File tree

22 files changed

+196
-72
lines changed

22 files changed

+196
-72
lines changed

Cabal/src/Distribution/Simple/GHC.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ buildOrReplLib mReplFlags verbosity numJobs pkg_descr lbi lib clbi = do
651651
distPref = fromFlag $ configDistPref $ configFlags lbi
652652
hpcdir way
653653
| forRepl = mempty -- HPC is not supported in ghci
654-
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way pkg_name
654+
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way
655655
| otherwise = mempty
656656

657657
createDirectoryIfMissingVerbose verbosity True libTargetDir
@@ -1548,7 +1548,7 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do
15481548
distPref = fromFlag $ configDistPref $ configFlags lbi
15491549
hpcdir way
15501550
| gbuildIsRepl bm = mempty -- HPC is not supported in ghci
1551-
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way (gbuildName bm)
1551+
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way
15521552
| otherwise = mempty
15531553

15541554
rpaths <- getRPaths lbi clbi

Cabal/src/Distribution/Simple/GHCJS.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,7 @@ buildOrReplLib mReplFlags verbosity numJobs pkg_descr lbi lib clbi = do
523523
distPref = fromFlag $ configDistPref $ configFlags lbi
524524
hpcdir way
525525
| forRepl = mempty -- HPC is not supported in ghci
526-
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way pkg_name
526+
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way
527527
| otherwise = mempty
528528

529529
createDirectoryIfMissingVerbose verbosity True libTargetDir
@@ -1243,7 +1243,7 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do
12431243
distPref = fromFlag $ configDistPref $ configFlags lbi
12441244
hpcdir way
12451245
| gbuildIsRepl bm = mempty -- HPC is not supported in ghci
1246-
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way (gbuildName bm)
1246+
| isCoverageEnabled = toFlag $ Hpc.mixDir distPref way
12471247
| otherwise = mempty
12481248

12491249
rpaths <- getRPaths lbi clbi

Cabal/src/Distribution/Simple/Hpc.hs

Lines changed: 92 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ import Distribution.Verbosity (Verbosity ())
4848
import Distribution.Version (anyVersion)
4949
import System.Directory (createDirectoryIfMissing, doesFileExist)
5050
import System.FilePath
51+
import Distribution.Types.LocalBuildInfo (componentNameCLBIs)
52+
import Distribution.Types.ComponentLocalBuildInfo (componentIsIndefinite)
5153

5254
-- -------------------------------------------------------------------------
5355
-- Haskell Program Coverage
@@ -73,44 +75,16 @@ mixDir
7375
-- ^ \"dist/\" prefix
7476
-> Way
7577
-> FilePath
76-
-- ^ Component name
77-
-> FilePath
7878
-- ^ Directory containing test suite's .mix files
79-
mixDir distPref way name = hpcDir distPrefBuild way </> "mix" </> name
80-
where
81-
-- This is a hack for HPC over test suites, needed to match the directory
82-
-- where HPC saves and reads .mix files when the main library of the same
83-
-- package is being processed, perhaps in a previous cabal run (#5213).
84-
-- E.g., @distPref@ may be
85-
-- @./dist-newstyle/build/x86_64-linux/ghc-9.0.1/cabal-gh5213-0.1/t/tests@
86-
-- but the path where library mix files reside has two less components
87-
-- at the end (@t/tests@) and this reduced path needs to be passed to
88-
-- both @hpc@ and @ghc@. For non-default optimization levels, the path
89-
-- suffix is one element longer and the extra path element needs
90-
-- to be preserved.
91-
distPrefElements = splitDirectories distPref
92-
distPrefBuild = case drop (length distPrefElements - 3) distPrefElements of
93-
["t", _, "noopt"] ->
94-
joinPath $
95-
take (length distPrefElements - 3) distPrefElements
96-
++ ["noopt"]
97-
["t", _, "opt"] ->
98-
joinPath $
99-
take (length distPrefElements - 3) distPrefElements
100-
++ ["opt"]
101-
[_, "t", _] ->
102-
joinPath $ take (length distPrefElements - 2) distPrefElements
103-
_ -> distPref
79+
mixDir distPref way = hpcDir distPref way </> "mix"
10480

10581
tixDir
10682
:: FilePath
10783
-- ^ \"dist/\" prefix
10884
-> Way
10985
-> FilePath
110-
-- ^ Component name
111-
-> FilePath
11286
-- ^ Directory containing test suite's .tix files
113-
tixDir distPref way name = hpcDir distPref way </> "tix" </> name
87+
tixDir distPref way = hpcDir distPref way </> "tix"
11488

11589
-- | Path to the .tix file containing a test suite's sum statistics.
11690
tixFilePath
@@ -121,17 +95,15 @@ tixFilePath
12195
-- ^ Component name
12296
-> FilePath
12397
-- ^ Path to test suite's .tix file
124-
tixFilePath distPref way name = tixDir distPref way name </> name <.> "tix"
98+
tixFilePath distPref way name = tixDir distPref way </> name <.> "tix"
12599

126100
htmlDir
127101
:: FilePath
128102
-- ^ \"dist/\" prefix
129103
-> Way
130104
-> FilePath
131-
-- ^ Component name
132-
-> FilePath
133105
-- ^ Path to test suite's HTML markup directory
134-
htmlDir distPref way name = hpcDir distPref way </> "html" </> name
106+
htmlDir distPref way = hpcDir distPref way </> "html"
135107

136108
-- | Attempt to guess the way the test suites in this package were compiled
137109
-- and linked with the library so the correct module interfaces are found.
@@ -146,14 +118,12 @@ markupTest
146118
:: Verbosity
147119
-> LocalBuildInfo
148120
-> FilePath
149-
-- ^ \"dist/\" prefix
150-
-> String
151-
-- ^ Library name
121+
-- ^ Testsuite \"dist/\" prefix
122+
-> PD.PackageDescription
152123
-> TestSuite
153-
-> Library
154124
-> IO ()
155-
markupTest verbosity lbi distPref libraryName suite library = do
156-
tixFileExists <- doesFileExist $ tixFilePath distPref way $ testName'
125+
markupTest verbosity lbi testDistPref pkg_descr suite = do
126+
tixFileExists <- doesFileExist $ tixFilePath testDistPref way $ testName'
157127
when tixFileExists $ do
158128
-- behaviour of 'markup' depends on version, so we need *a* version
159129
-- but no particular one
@@ -163,35 +133,36 @@ markupTest verbosity lbi distPref libraryName suite library = do
163133
hpcProgram
164134
anyVersion
165135
(withPrograms lbi)
166-
let htmlDir_ = htmlDir distPref way testName'
136+
let htmlDir_ = htmlDir testDistPref way
167137
markup
168138
hpc
169139
hpcVer
170140
verbosity
171-
(tixFilePath distPref way testName')
141+
(tixFilePath testDistPref way testName')
172142
mixDirs
173143
htmlDir_
174-
(exposedModules library)
144+
included
175145
notice verbosity $
176146
"Test coverage report written to "
177147
++ htmlDir_
178148
</> "hpc_index" <.> "html"
179149
where
180150
way = guessWay lbi
181151
testName' = unUnqualComponentName $ testName suite
182-
mixDirs = map (mixDir distPref way) [testName', libraryName]
152+
mixDirs = mixDir testDistPref way : map (libMixDir way testDistPref) (nonIndefiniteLibraries lbi pkg_descr)
153+
included = concatMap exposedModules $ nonIndefiniteLibraries lbi pkg_descr
183154

184155
-- | Generate the HTML markup for all of a package's test suites.
185156
markupPackage
186157
:: Verbosity
187158
-> LocalBuildInfo
188159
-> FilePath
189-
-- ^ \"dist/\" prefix
160+
-- ^ Testsuite \"dist/\" prefix
190161
-> PD.PackageDescription
191162
-> [TestSuite]
192163
-> IO ()
193-
markupPackage verbosity lbi distPref pkg_descr suites = do
194-
let tixFiles = map (tixFilePath distPref way) testNames
164+
markupPackage verbosity lbi testDistPref pkg_descr suites = do
165+
let tixFiles = map (tixFilePath testDistPref way) testNames
195166
tixFilesExist <- traverse doesFileExist tixFiles
196167
when (and tixFilesExist) $ do
197168
-- behaviour of 'markup' depends on version, so we need *a* version
@@ -202,8 +173,8 @@ markupPackage verbosity lbi distPref pkg_descr suites = do
202173
hpcProgram
203174
anyVersion
204175
(withPrograms lbi)
205-
let outFile = tixFilePath distPref way libraryName
206-
htmlDir' = htmlDir distPref way libraryName
176+
let outFile = tixFilePath testDistPref way pkgName
177+
htmlDir' = htmlDir testDistPref way
207178
excluded = concatMap testModules suites ++ [main]
208179
createDirectoryIfMissing True $ takeDirectory outFile
209180
union hpc verbosity tixFiles outFile excluded
@@ -215,6 +186,75 @@ markupPackage verbosity lbi distPref pkg_descr suites = do
215186
where
216187
way = guessWay lbi
217188
testNames = fmap (unUnqualComponentName . testName) suites
218-
mixDirs = map (mixDir distPref way) $ libraryName : testNames
219-
included = concatMap (exposedModules) $ PD.allLibraries pkg_descr
220-
libraryName = prettyShow $ PD.package pkg_descr
189+
pkgName = prettyShow $ PD.package pkg_descr
190+
mixDirs = mixDir testDistPref way : map (libMixDir way testDistPref) (nonIndefiniteLibraries lbi pkg_descr)
191+
included = concatMap exposedModules $ nonIndefiniteLibraries lbi pkg_descr
192+
193+
-- | Get all the non-indefinite libraries in a package, ignoring indefinite
194+
-- components and their instantiations.
195+
-- HPC doesn't support backpack (eg. would does it mean to cover a module of a
196+
-- library that is instantiated in two different ways?), so we invoke this
197+
-- function to determine which library hpc dirs to add to the include path of
198+
-- the hpc markup command.
199+
nonIndefiniteLibraries :: LocalBuildInfo -> PD.PackageDescription -> [Library]
200+
nonIndefiniteLibraries lbi pkg_desc
201+
= [ lib
202+
| lib <- PD.allLibraries pkg_desc
203+
-- We only care about libraries with exactly one CLBI.
204+
-- If there were more than one CLBI, they would be the indefinite CLBI and
205+
-- the multiple instantiations.
206+
-- Because backpack is unsupported by hpc, we ignore all indefinite components
207+
, [c] <- pure $ componentNameCLBIs lbi (PD.CLibName (libName lib))
208+
, not $ componentIsIndefinite c
209+
]
210+
211+
-- | Determine the path to the library's `.mix` dir for the given way
212+
libMixDir :: Way
213+
-> FilePath
214+
-- ^ Testsuite dist-dir prefix (needed in the pathToLibHpc hack)
215+
-> Library
216+
-> FilePath
217+
libMixDir way testDistPref lib = pathToLibHpc testDistPref (PD.libName lib) `mixDir` way
218+
219+
220+
-- | A (non-exported) hack to determine the path to the main and internal libs
221+
-- directory given the testsuite's dist prefix.
222+
--
223+
-- We use this function when constructing calls to `hpc markup` since otherwise
224+
-- having cabal-install communicate the path to the main and sub libraries
225+
-- dist-dir when building the test component, via the Setup.hs interface, is
226+
-- far more complicated.
227+
pathToLibHpc :: FilePath -> PD.LibraryName -> FilePath
228+
pathToLibHpc testDistPref libname = distPrefLib
229+
where
230+
-- This is a hack for HPC over test suites, needed to match the directory
231+
-- where HPC saves and reads .mix files when the main library of the same
232+
-- package is being processed, perhaps in a previous cabal run (#5213).
233+
-- E.g., @distPref@ may be
234+
-- @./dist-newstyle/build/x86_64-linux/ghc-9.0.1/cabal-gh5213-0.1/t/tests@
235+
-- but the path where library mix files reside has two less components
236+
-- at the end (@t/tests@) and this reduced path needs to be passed to
237+
-- both @hpc@ and @ghc@. For non-default optimization levels, the path
238+
-- suffix is one element longer and the extra path element needs
239+
-- to be preserved.
240+
distPrefElements = splitDirectories testDistPref
241+
distPrefLib = case drop (length distPrefElements - 3) distPrefElements of
242+
["t", _, "noopt"] ->
243+
joinPath $
244+
take (length distPrefElements - 3) distPrefElements
245+
++ [distSuffixInternalLib]
246+
++ ["noopt"]
247+
["t", _, "opt"] ->
248+
joinPath $
249+
take (length distPrefElements - 3) distPrefElements
250+
++ [distSuffixInternalLib]
251+
++ ["opt"]
252+
[_, "t", _] ->
253+
joinPath $
254+
take (length distPrefElements - 2) distPrefElements
255+
++ [distSuffixInternalLib]
256+
_ -> error "pathToLibHpc: Expecting `testDirPref` to be the dist prefix of a test-suite component"
257+
distSuffixInternalLib = case libname of
258+
PD.LMainLibName -> ""
259+
PD.LSubLibName slname -> "l" </> unUnqualComponentName slname
260+

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import Prelude ()
1010

1111
import Distribution.Compat.Environment
1212
import qualified Distribution.PackageDescription as PD
13-
import Distribution.Pretty
1413
import Distribution.Simple.Build.PathsModule
1514
import Distribution.Simple.BuildPaths
1615
import Distribution.Simple.Compiler
@@ -51,7 +50,7 @@ runTest
5150
runTest pkg_descr lbi clbi flags suite = do
5251
let isCoverageEnabled = LBI.testCoverage lbi
5352
way = guessWay lbi
54-
tixDir_ = tixDir distPref way testName'
53+
tixDir_ = tixDir distPref way
5554

5655
pwd <- getCurrentDirectory
5756
existingEnv <- getEnvironment
@@ -174,8 +173,8 @@ runTest pkg_descr lbi clbi flags suite = do
174173
case PD.library pkg_descr of
175174
Nothing ->
176175
dieWithException verbosity TestCoverageSupport
177-
Just library ->
178-
markupTest verbosity lbi distPref (prettyShow $ PD.package pkg_descr) suite library
176+
Just _library ->
177+
markupTest verbosity lbi distPref pkg_descr suite
179178

180179
return suiteLog
181180
where

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,12 @@ runTest pkg_descr lbi clbi flags suite = do
8080

8181
-- Remove old .tix files if appropriate.
8282
unless (fromFlag $ testKeepTix flags) $ do
83-
let tDir = tixDir distPref way testName'
83+
let tDir = tixDir distPref way
8484
exists' <- doesDirectoryExist tDir
8585
when exists' $ removeDirectoryRecursive tDir
8686

8787
-- Create directory for HPC files.
88-
createDirectoryIfMissing True $ tixDir distPref way testName'
88+
createDirectoryIfMissing True $ tixDir distPref way
8989

9090
-- Write summary notices indicating start of test suite
9191
notice verbosity $ summarizeSuiteStart testName'
@@ -189,8 +189,8 @@ runTest pkg_descr lbi clbi flags suite = do
189189
case PD.library pkg_descr of
190190
Nothing ->
191191
dieWithException verbosity TestCoverageSupportLibV09
192-
Just library ->
193-
markupTest verbosity lbi distPref (prettyShow $ PD.package pkg_descr) suite library
192+
Just _library ->
193+
markupTest verbosity lbi distPref pkg_descr suite
194194

195195
return suiteLog
196196
where

Cabal/src/Distribution/Types/LocalBuildInfo.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ data LocalBuildInfo = LocalBuildInfo
108108
, componentNameMap :: Map ComponentName [ComponentLocalBuildInfo]
109109
-- ^ A map from component name to all matching
110110
-- components. These coincide with 'componentGraph'
111+
-- There may be more than one matching component because of backpack instantiations
111112
, promisedPkgs :: Map (PackageName, ComponentName) ComponentId
112113
-- ^ The packages we were promised, but aren't already installed.
113114
-- MP: Perhaps this just needs to be a Set UnitId at this stage.

cabal-install/src/Distribution/Client/ProjectPlanning.hs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,7 +1673,7 @@ elaborateInstallPlan
16731673
where
16741674
-- You are eligible to per-component build if this list is empty
16751675
why_not_per_component g =
1676-
cuz_buildtype ++ cuz_spec ++ cuz_length ++ cuz_flag ++ cuz_coverage
1676+
cuz_buildtype ++ cuz_spec ++ cuz_length ++ cuz_flag
16771677
where
16781678
cuz reason = [text reason]
16791679
-- We have to disable per-component for now with
@@ -1710,12 +1710,6 @@ elaborateInstallPlan
17101710
| fromFlagOrDefault True (projectConfigPerComponent sharedPackageConfig) =
17111711
[]
17121712
| otherwise = cuz "you passed --disable-per-component"
1713-
-- Enabling program coverage introduces odd runtime dependencies
1714-
-- between components.
1715-
cuz_coverage
1716-
| fromFlagOrDefault False (packageConfigCoverage localPackagesConfig) =
1717-
cuz "program coverage is enabled"
1718-
| otherwise = []
17191713

17201714
-- \| Sometimes a package may make use of features which are only
17211715
-- supported in per-package mode. If this is the case, we should

cabal-testsuite/PackageTests/Backpack/Includes2/Includes2.cabal

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,10 @@ executable exe
3939
main-is: Main.hs
4040
hs-source-dirs: exe
4141
default-language: Haskell2010
42+
43+
test-suite test
44+
type: exitcode-stdio-1.0
45+
build-depends: base, Includes2
46+
main-is: test.hs
47+
hs-source-dirs: test
48+
default-language: Haskell2010
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import Test.Cabal.Prelude
2+
main = cabalTest $ do
3+
skipUnlessGhcVersion ">= 8.1"
4+
cabal "test" ["--enable-coverage"]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
cabal-version: 3.0
2+
name: T4798
3+
version: 0.1
4+
5+
library
6+
exposed-modules: U2F, U2F.Types
7+
ghc-options: -Wall
8+
build-depends: base
9+
hs-source-dirs: src
10+
default-language: Haskell2010
11+
12+
test-suite hspec-suite
13+
type: exitcode-stdio-1.0
14+
main-is: test.hs
15+
ghc-options: -Wall
16+
hs-source-dirs: tests
17+
default-language: Haskell2010
18+
build-depends: base, T4798
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
packages: .
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Test.Cabal.Prelude
2+
main = cabalTest $ cabal "test" ["--enable-coverage"]
3+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module U2F where
2+
3+
import U2F.Types
4+
5+
ourCurve :: String
6+
ourCurve = show SEC_p256r1

0 commit comments

Comments
 (0)