diff --git a/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs b/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs index 18fbcf549af..bb3aca57258 100644 --- a/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs +++ b/Cabal-tests/tests/UnitTests/Distribution/Utils/Structured.hs @@ -41,7 +41,7 @@ md5CheckGenericPackageDescription proxy = md5Check proxy md5CheckLocalBuildInfo :: Proxy LocalBuildInfo -> Assertion md5CheckLocalBuildInfo proxy = md5Check proxy #if MIN_VERSION_base(4,19,0) - 0x205fbe2649bc5e488bce50c07a71cadb + 0x512e880894570552f08aa82547568dbc #else - 0x26e91a71ebd19d4d6ce37f798ede249a + 0x968807984ad42d41a9e9ab696a9fec58 #endif diff --git a/Cabal/src/Distribution/Simple/Configure.hs b/Cabal/src/Distribution/Simple/Configure.hs index b7aabf65f18..24d02e01fd3 100644 --- a/Cabal/src/Distribution/Simple/Configure.hs +++ b/Cabal/src/Distribution/Simple/Configure.hs @@ -1,5 +1,7 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} @@ -44,6 +46,7 @@ module Distribution.Simple.Configure , localBuildInfoFile , getInstalledPackages , getInstalledPackagesMonitorFiles + , getInstalledPackageById , getPackageDBContents , configCompilerEx , configCompilerAuxEx @@ -78,7 +81,7 @@ import Distribution.Simple.BuildTarget import Distribution.Simple.BuildToolDepends import Distribution.Simple.Compiler import Distribution.Simple.LocalBuildInfo -import Distribution.Simple.PackageIndex (InstalledPackageIndex) +import Distribution.Simple.PackageIndex (InstalledPackageIndex, lookupUnitId) import qualified Distribution.Simple.PackageIndex as PackageIndex import Distribution.Simple.PreProcess import Distribution.Simple.Program @@ -877,10 +880,21 @@ configure (pkg_descr0, pbi) cfg = do Map.empty buildComponents + -- For whole-package configure, we have to determine the additional + -- configCoverageFor of the main lib and sub libs here. + let extraCoverageFor :: [UnitId] = case enabled of + -- Whole package configure, add package libs + ComponentRequestedSpec{} -> mapMaybe (\case LibComponentLocalBuildInfo{componentUnitId} -> Just componentUnitId; _ -> Nothing) buildComponents + -- Component configure, no need to do anything + OneComponentRequestedSpec{} -> [] + + -- TODO: Should we also enforce something here on that --coverage-for cannot + -- include indefinite components or instantiations? + let lbi = (setCoverageLBI . setProfLBI) LocalBuildInfo - { configFlags = cfg + { configFlags = cfg{configCoverageFor = configCoverageFor cfg <> toFlag extraCoverageFor} , flagAssignment = flags , componentEnabledSpec = enabled , extraConfigArgs = [] -- Currently configure does not @@ -1747,6 +1761,13 @@ getInstalledPackagesMonitorFiles verbosity comp packageDBs progdb platform = ++ prettyShow other return [] +-- | Looks up the 'InstalledPackageInfo' of a given 'UnitId' from the +-- 'PackageDBStack' in the 'LocalBuildInfo'. +getInstalledPackageById :: Verbosity -> LocalBuildInfo -> UnitId -> IO (Maybe InstalledPackageInfo) +getInstalledPackageById verbosity LocalBuildInfo{compiler, withPackageDB, withPrograms} unitid = do + ipindex <- getInstalledPackages verbosity compiler withPackageDB withPrograms + return $ lookupUnitId ipindex unitid + -- | The user interface specifies the package dbs to use with a combination of -- @--global@, @--user@ and @--package-db=global|user|clear|$file@. -- This function combines the global/user flag and interprets the package-db diff --git a/Cabal/src/Distribution/Simple/GHC/BuildGeneric.hs b/Cabal/src/Distribution/Simple/GHC/BuildGeneric.hs index 16bd94294ee..7ff326aa9b3 100644 --- a/Cabal/src/Distribution/Simple/GHC/BuildGeneric.hs +++ b/Cabal/src/Distribution/Simple/GHC/BuildGeneric.hs @@ -19,7 +19,6 @@ import Distribution.PackageDescription.Utils (cabalBug) import Distribution.Pretty import Distribution.Simple.BuildPaths import Distribution.Simple.Compiler -import Distribution.Simple.Flag (Flag (..), fromFlag, toFlag) import Distribution.Simple.GHC.Build ( checkNeedsRecompilation , componentGhcOptions @@ -39,7 +38,7 @@ import Distribution.Simple.LocalBuildInfo import qualified Distribution.Simple.PackageIndex as PackageIndex import Distribution.Simple.Program import Distribution.Simple.Program.GHC -import Distribution.Simple.Setup.Config +import Distribution.Simple.Setup.Common import Distribution.Simple.Setup.Repl import Distribution.Simple.Utils import Distribution.System @@ -399,10 +398,9 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do -- Determine if program coverage should be enabled and if so, what -- '-hpcdir' should be. let isCoverageEnabled = exeCoverage lbi - distPref = fromFlag $ configDistPref $ configFlags lbi hpcdir way | gbuildIsRepl bm = mempty -- HPC is not supported in ghci - | isCoverageEnabled = toFlag $ Hpc.mixDir distPref way + | isCoverageEnabled = toFlag $ Hpc.mixDir (tmpDir extraCompilationArtifacts) way | otherwise = mempty rpaths <- getRPaths lbi clbi diff --git a/Cabal/src/Distribution/Simple/GHC/BuildOrRepl.hs b/Cabal/src/Distribution/Simple/GHC/BuildOrRepl.hs index bfb9ddfa09c..8ae87642b51 100644 --- a/Cabal/src/Distribution/Simple/GHC/BuildOrRepl.hs +++ b/Cabal/src/Distribution/Simple/GHC/BuildOrRepl.hs @@ -9,7 +9,6 @@ import Distribution.Package import Distribution.PackageDescription as PD import Distribution.Simple.BuildPaths import Distribution.Simple.Compiler -import Distribution.Simple.Flag (Flag (..), fromFlag, toFlag) import Distribution.Simple.GHC.Build ( checkNeedsRecompilation , componentGhcOptions @@ -27,7 +26,7 @@ import Distribution.Simple.Program import qualified Distribution.Simple.Program.Ar as Ar import Distribution.Simple.Program.GHC import qualified Distribution.Simple.Program.Ld as Ld -import Distribution.Simple.Setup.Config +import Distribution.Simple.Setup.Common import Distribution.Simple.Setup.Repl import Distribution.Simple.Utils import Distribution.System @@ -96,10 +95,9 @@ buildOrReplLib mReplFlags verbosity numJobs pkg_descr lbi lib clbi = do -- Determine if program coverage should be enabled and if so, what -- '-hpcdir' should be. let isCoverageEnabled = libCoverage lbi - distPref = fromFlag $ configDistPref $ configFlags lbi hpcdir way | forRepl = mempty -- HPC is not supported in ghci - | isCoverageEnabled = toFlag $ Hpc.mixDir distPref way + | isCoverageEnabled = toFlag $ Hpc.mixDir (libTargetDir extraCompilationArtifacts) way | otherwise = mempty createDirectoryIfMissingVerbose verbosity True libTargetDir diff --git a/Cabal/src/Distribution/Simple/GHCJS.hs b/Cabal/src/Distribution/Simple/GHCJS.hs index 1c4d899812d..53f78b7e5e6 100644 --- a/Cabal/src/Distribution/Simple/GHCJS.hs +++ b/Cabal/src/Distribution/Simple/GHCJS.hs @@ -72,7 +72,7 @@ import Distribution.Simple.Program import Distribution.Simple.Program.GHC import qualified Distribution.Simple.Program.HcPkg as HcPkg import qualified Distribution.Simple.Program.Strip as Strip -import Distribution.Simple.Setup.Config +import Distribution.Simple.Setup.Common import Distribution.Simple.Utils import Distribution.System import Distribution.Types.ComponentLocalBuildInfo @@ -515,10 +515,9 @@ buildOrReplLib mReplFlags verbosity numJobs _pkg_descr lbi lib clbi = do -- Determine if program coverage should be enabled and if so, what -- '-hpcdir' should be. let isCoverageEnabled = libCoverage lbi - distPref = fromFlag $ configDistPref $ configFlags lbi hpcdir way | forRepl = mempty -- HPC is not supported in ghci - | isCoverageEnabled = toFlag $ Hpc.mixDir distPref way + | isCoverageEnabled = toFlag $ Hpc.mixDir (libTargetDir extraCompilationArtifacts) way | otherwise = mempty createDirectoryIfMissingVerbose verbosity True libTargetDir @@ -1235,10 +1234,9 @@ gbuild verbosity numJobs pkg_descr lbi bm clbi = do -- Determine if program coverage should be enabled and if so, what -- '-hpcdir' should be. let isCoverageEnabled = exeCoverage lbi - distPref = fromFlag $ configDistPref $ configFlags lbi hpcdir way | gbuildIsRepl bm = mempty -- HPC is not supported in ghci - | isCoverageEnabled = toFlag $ Hpc.mixDir distPref way + | isCoverageEnabled = toFlag $ Hpc.mixDir (tmpDir extraCompilationArtifacts) way | otherwise = mempty rpaths <- getRPaths lbi clbi diff --git a/Cabal/src/Distribution/Simple/Hpc.hs b/Cabal/src/Distribution/Simple/Hpc.hs index e39b75d4ae4..485c999727a 100644 --- a/Cabal/src/Distribution/Simple/Hpc.hs +++ b/Cabal/src/Distribution/Simple/Hpc.hs @@ -28,21 +28,19 @@ module Distribution.Simple.Hpc import Distribution.Compat.Prelude import Prelude () -import Distribution.ModuleName (main) +import Distribution.ModuleName (ModuleName, main) import Distribution.PackageDescription ( TestSuite (..) , testModules ) import qualified Distribution.PackageDescription as PD import Distribution.Pretty -import Distribution.Simple.Flag (fromFlagOrDefault) import Distribution.Simple.LocalBuildInfo (LocalBuildInfo (..)) import Distribution.Simple.Program ( hpcProgram , requireProgramVersion ) import Distribution.Simple.Program.Hpc (markup, union) -import Distribution.Simple.Setup (TestFlags (..)) import Distribution.Simple.Utils (notice) import Distribution.Types.UnqualComponentName import Distribution.Verbosity (Verbosity ()) @@ -115,14 +113,15 @@ guessWay lbi -- | Generate the HTML markup for a package's test suites. markupPackage :: Verbosity - -> TestFlags + -> [FilePath] + -> [ModuleName] -> LocalBuildInfo -> FilePath -- ^ Testsuite \"dist/\" prefix -> PD.PackageDescription -> [TestSuite] -> IO () -markupPackage verbosity TestFlags{testCoverageDistPrefs, testCoverageLibsModules} lbi testDistPref pkg_descr suites = do +markupPackage verbosity testCoverageDistPrefs testCoverageLibsModules lbi testDistPref pkg_descr suites = do let tixFiles = map (tixFilePath testDistPref way) testNames tixFilesExist <- traverse doesFileExist tixFiles when (and tixFilesExist) $ do @@ -168,5 +167,5 @@ markupPackage verbosity TestFlags{testCoverageDistPrefs, testCoverageLibsModules where way = guessWay lbi testNames = fmap (unUnqualComponentName . testName) suites - mixDirs = map (`mixDir` way) (fromFlagOrDefault [] testCoverageDistPrefs) - included = fromFlagOrDefault [] testCoverageLibsModules + mixDirs = map (`mixDir` way) (testCoverageDistPrefs) + included = testCoverageLibsModules diff --git a/Cabal/src/Distribution/Simple/Setup/Config.hs b/Cabal/src/Distribution/Simple/Setup/Config.hs index 05fd07f33ca..30267bbfd76 100644 --- a/Cabal/src/Distribution/Simple/Setup/Config.hs +++ b/Cabal/src/Distribution/Simple/Setup/Config.hs @@ -54,6 +54,7 @@ import Distribution.Types.DumpBuildInfo import Distribution.Types.GivenComponent import Distribution.Types.Module import Distribution.Types.PackageVersionConstraint +import Distribution.Types.UnitId import Distribution.Utils.NubList import Distribution.Verbosity import qualified Text.PrettyPrint as Disp @@ -220,6 +221,11 @@ data ConfigFlags = ConfigFlags -- ^ Allow depending on private sublibraries. This is used by external -- tools (like cabal-install) so they can add multiple-public-libraries -- compatibility to older ghcs by checking visibility externally. + , configCoverageFor :: Flag [UnitId] + -- ^ The list of libraries to be included in the hpc coverage report for + -- testsuites run with @--enable-coverage@. Notably, this list must exclude + -- indefinite libraries and instantiations because HPC does not support + -- backpack (Nov. 2023). } deriving (Generic, Read, Show, Typeable) @@ -288,6 +294,7 @@ instance Eq ConfigFlags where && equal configDebugInfo && equal configDumpBuildInfo && equal configUseResponseFiles + && equal configCoverageFor where equal f = on (==) f a b @@ -828,6 +835,22 @@ configureOptions showOrParseArgs = configAllowDependingOnPrivateLibs (\v flags -> flags{configAllowDependingOnPrivateLibs = v}) trueArg + , option + "" + ["coverage-for"] + "Module of a project-local library to include in the HPC report" + configCoverageFor + ( \v flags -> + flags + { configCoverageFor = + mergeListFlag (configCoverageFor flags) v + } + ) + ( reqArg' + "MODULE" + (Flag . (: []) . fromString) + (fmap prettyShow . fromFlagOrDefault []) + ) ] where liftInstallDirs = diff --git a/Cabal/src/Distribution/Simple/Setup/Test.hs b/Cabal/src/Distribution/Simple/Setup/Test.hs index c2fa3f14d6d..bee0ccbef1a 100644 --- a/Cabal/src/Distribution/Simple/Setup/Test.hs +++ b/Cabal/src/Distribution/Simple/Setup/Test.hs @@ -40,7 +40,6 @@ import Distribution.Simple.Utils import Distribution.Verbosity import qualified Text.PrettyPrint as Disp -import Distribution.ModuleName (ModuleName) import Distribution.Simple.Setup.Common -- ------------------------------------------------------------ @@ -89,15 +88,6 @@ data TestFlags = TestFlags , testKeepTix :: Flag Bool , testWrapper :: Flag FilePath , testFailWhenNoTestSuites :: Flag Bool - , testCoverageLibsModules :: Flag [ModuleName] - -- ^ The list of all modules from libraries in the local project that should - -- be included in the hpc coverage report. - , testCoverageDistPrefs :: Flag [FilePath] - -- ^ The path to each library local to this project and to the test - -- components being built, to include in coverage reporting (notably, this - -- excludes indefinite libraries and instantiations because HPC does not - -- support backpack - Nov. 2023). Cabal uses these paths as dist prefixes to - -- determine the path to the `mix` dirs of each component to cover. , -- TODO: think about if/how options are passed to test exes testOptions :: [PathTemplate] } @@ -114,8 +104,6 @@ defaultTestFlags = , testKeepTix = toFlag False , testWrapper = NoFlag , testFailWhenNoTestSuites = toFlag False - , testCoverageLibsModules = NoFlag - , testCoverageDistPrefs = NoFlag , testOptions = [] } @@ -221,38 +209,6 @@ testOptions' showOrParseArgs = testFailWhenNoTestSuites (\v flags -> flags{testFailWhenNoTestSuites = v}) trueArg - , option - [] - ["coverage-module"] - "Module of a project-local library to include in the HPC report" - testCoverageLibsModules - ( \v flags -> - flags - { testCoverageLibsModules = - mergeListFlag (testCoverageLibsModules flags) v - } - ) - ( reqArg' - "MODULE" - (Flag . (: []) . fromString) - (fmap prettyShow . fromFlagOrDefault []) - ) - , option - [] - ["coverage-dist-dir"] - "The directory where Cabal puts generated build files of an HPC enabled component" - testCoverageDistPrefs - ( \v flags -> - flags - { testCoverageDistPrefs = - mergeListFlag (testCoverageDistPrefs flags) v - } - ) - ( reqArg' - "DIR" - (Flag . (: [])) - (fromFlagOrDefault []) - ) , option [] ["test-options"] diff --git a/Cabal/src/Distribution/Simple/Test.hs b/Cabal/src/Distribution/Simple/Test.hs index 62158ece57f..1f601fa1660 100644 --- a/Cabal/src/Distribution/Simple/Test.hs +++ b/Cabal/src/Distribution/Simple/Test.hs @@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ViewPatterns #-} ----------------------------------------------------------------------------- @@ -21,6 +22,8 @@ module Distribution.Simple.Test import Distribution.Compat.Prelude import Prelude () +import Control.Monad +import Distribution.ModuleName import qualified Distribution.PackageDescription as PD import Distribution.Pretty import Distribution.Simple.Compiler @@ -38,7 +41,14 @@ import Distribution.TestSuite import qualified Distribution.Types.LocalBuildInfo as LBI import Distribution.Types.UnqualComponentName +import Distribution.Simple.Configure (getInstalledPackageById) import Distribution.Simple.Errors +import Distribution.Simple.Setup (fromFlagOrDefault) +import Distribution.Simple.Setup.Common (extraCompilationArtifacts) +import Distribution.Simple.Setup.Config +import Distribution.Types.ExposedModule +import Distribution.Types.InstalledPackageInfo (InstalledPackageInfo (libraryDirs), exposedModules) +import Distribution.Types.LocalBuildInfo (LocalBuildInfo (..)) import System.Directory ( createDirectoryIfMissing , doesFileExist @@ -68,16 +78,20 @@ test args pkg_descr lbi flags = do enabledTests = LBI.enabledTestLBIs pkg_descr lbi doTest - :: ( (PD.TestSuite, LBI.ComponentLocalBuildInfo) + :: [FilePath] + -- \^ The paths to the library components to include in the coverage report + -> [ModuleName] + -- \^ The modules to include in the coverage report + -> ( (PD.TestSuite, LBI.ComponentLocalBuildInfo) , Maybe TestSuiteLog ) -> IO TestSuiteLog - doTest ((suite, clbi), _) = + doTest testCoverageDistPrefs testCoverageLibsModules ((suite, clbi), _) = case PD.testInterface suite of PD.TestSuiteExeV10 _ _ -> - ExeV10.runTest pkg_descr lbi clbi flags suite + ExeV10.runTest pkg_descr lbi clbi testCoverageDistPrefs testCoverageLibsModules flags suite PD.TestSuiteLibV09 _ _ -> - LibV09.runTest pkg_descr lbi clbi flags suite + LibV09.runTest pkg_descr lbi clbi testCoverageDistPrefs testCoverageLibsModules flags suite _ -> return TestSuiteLog @@ -122,9 +136,55 @@ test args pkg_descr lbi flags = do >>= filterM doesFileExist . map (testLogDir ) >>= traverse_ removeFile + -- We configured the unit-ids of libraries we should cover in our coverage + -- report at configure time into the local build info. At build time, we built + -- the hpc artifacts into the extraCompilationArtifacts directory, which, at + -- install time, is copied into the ghc-pkg database files. + -- Now, we get the path to the HPC artifacts and exposed modules of each + -- library by querying the package database keyed by unit-id: + let coverageFor = fromFlagOrDefault [] (configCoverageFor (configFlags lbi)) + ipkginfos <- catMaybes <$> mapM (getInstalledPackageById verbosity lbi) coverageFor + let ( concat -> testCoverageDistPrefs0 + , concat -> testCoverageLibsModules + ) = + -- ROMES:TODO: Is it right to use libraryDirs here? When do we have + -- more than one library dir for an installed package? + unzip $ + map + ( \ip -> + ( map ( extraCompilationArtifacts) $ libraryDirs ip + , map exposedName $ exposedModules ip + ) + ) + ipkginfos + + -- Additionally, we find the path to the testsuites' hpc build artifacts + suitesInstalledInfo <- + catMaybes + <$> mapM + ( getInstalledPackageById verbosity lbi + . LBI.componentUnitId + . snd + . fst + ) + testsToRun + let testCoverageDistPrefs = + testCoverageDistPrefs0 + ++ ( concat + . map + ( map ( extraCompilationArtifacts) + . libraryDirs + ) + $ suitesInstalledInfo + ) + print "SUITES INSTALLED INFO" + print suitesInstalledInfo + print "TEST COVERAGE DIST PREFS" + print testCoverageDistPrefs + let totalSuites = length testsToRun notice verbosity $ "Running " ++ show totalSuites ++ " test suites..." - suites <- traverse doTest testsToRun + suites <- traverse (doTest testCoverageDistPrefs testCoverageLibsModules) testsToRun let packageLog = (localPackageLog pkg_descr lbi){testSuites = suites} packageLogFile = () testLogDir $ @@ -133,7 +193,7 @@ test args pkg_descr lbi flags = do writeFile packageLogFile $ show packageLog when (LBI.testCoverage lbi) $ - markupPackage verbosity flags lbi distPref pkg_descr $ + markupPackage verbosity testCoverageDistPrefs testCoverageLibsModules lbi distPref pkg_descr $ map (fst . fst) testsToRun unless allOk exitFailure diff --git a/Cabal/src/Distribution/Simple/Test/ExeV10.hs b/Cabal/src/Distribution/Simple/Test/ExeV10.hs index af734c5123e..cde232588ed 100644 --- a/Cabal/src/Distribution/Simple/Test/ExeV10.hs +++ b/Cabal/src/Distribution/Simple/Test/ExeV10.hs @@ -9,6 +9,7 @@ import Distribution.Compat.Prelude import Prelude () import Distribution.Compat.Environment +import Distribution.ModuleName import qualified Distribution.PackageDescription as PD import Distribution.Simple.Build.PathsModule import Distribution.Simple.BuildPaths @@ -44,10 +45,14 @@ runTest :: PD.PackageDescription -> LBI.LocalBuildInfo -> LBI.ComponentLocalBuildInfo + -> [FilePath] + -- ^ The paths to the library components to include in the coverage report + -> [ModuleName] + -- ^ The modules to include in the coverage report -> TestFlags -> PD.TestSuite -> IO TestSuiteLog -runTest pkg_descr lbi clbi flags suite = do +runTest pkg_descr lbi clbi testCoverageDistPrefs testCoverageLibModules flags suite = do let isCoverageEnabled = LBI.testCoverage lbi way = guessWay lbi tixDir_ = tixDir distPref way @@ -178,7 +183,7 @@ runTest pkg_descr lbi clbi flags suite = do when (null $ PD.allLibraries pkg_descr) $ dieWithException verbosity TestCoverageSupport - markupPackage verbosity flags lbi distPref pkg_descr [suite] + markupPackage verbosity testCoverageDistPrefs testCoverageLibModules lbi distPref pkg_descr [suite] return suiteLog where diff --git a/Cabal/src/Distribution/Simple/Test/LibV09.hs b/Cabal/src/Distribution/Simple/Test/LibV09.hs index 3204ce12227..347d07e1510 100644 --- a/Cabal/src/Distribution/Simple/Test/LibV09.hs +++ b/Cabal/src/Distribution/Simple/Test/LibV09.hs @@ -58,10 +58,14 @@ runTest :: PD.PackageDescription -> LBI.LocalBuildInfo -> LBI.ComponentLocalBuildInfo + -> [FilePath] + -- ^ The paths to the library components to include in the coverage report + -> [ModuleName] + -- ^ The modules to include in the coverage report -> TestFlags -> PD.TestSuite -> IO TestSuiteLog -runTest pkg_descr lbi clbi flags suite = do +runTest pkg_descr lbi clbi testCoverageDistPrefs testCoverageLibModules flags suite = do let isCoverageEnabled = LBI.testCoverage lbi way = guessWay lbi @@ -194,7 +198,7 @@ runTest pkg_descr lbi clbi flags suite = do when (null $ PD.allLibraries pkg_descr) $ dieWithException verbosity TestCoverageSupport - markupPackage verbosity flags lbi distPref pkg_descr [suite] + markupPackage verbosity testCoverageDistPrefs testCoverageLibModules lbi distPref pkg_descr [suite] return suiteLog where diff --git a/Plan.md b/Plan.md new file mode 100644 index 00000000000..7f96c4f99fc --- /dev/null +++ b/Plan.md @@ -0,0 +1,34 @@ +Plan: + +[x] cabal-install determines units to cover +[ ] Cabal determines units to cover on its own when it is being built per package +[x] cabal-install passes --coverage-for= to ./Setup configure for each + component that we configure + +[x] Cabal configure accepts --coverage-for= + +-- For libraries with --enable-coverage +[x] ./Setup build will write the hpc directories into extraSomethingArtifacts +[x] ./Setup install will copy the extraSomethingArtifacts to the pkg-db entry for + that component + +-- For testsuite +[x] ./Setup test will read all --coverage-for= from LBI +[x] Then read the hpc directories from the extraSomethingArtifacts for each of + the units-ids (that should be installed) +[x] Then pass those directories to the `hpc markup --hpcdir=$(ghc-pkg read unit-id | ls hpc)` call + + +-- Whole package +./Setup configure --coverage-for= + +[ ] How do we handle interface files in whole-package setup configure? That's + basically what we need. + +[ ] In the commit message mention interface file analogy + +TODO: + +[ ] Are we still including the test directory in the markup call? (No, it doesn't seem like we do despite adding the suites something...) +[ ] Do we really need the test directory in the markup, if we don't include any module from the library? It seems to be working fine! +[ ] Whole package builds must determine during configure the units to cover from that package diff --git a/cabal-install/src/Distribution/Client/Config.hs b/cabal-install/src/Distribution/Client/Config.hs index feaf10a6360..0fe93081bd7 100644 --- a/cabal-install/src/Distribution/Client/Config.hs +++ b/cabal-install/src/Distribution/Client/Config.hs @@ -531,6 +531,7 @@ instance Semigroup SavedConfig where , configDumpBuildInfo = combine configDumpBuildInfo , configAllowDependingOnPrivateLibs = combine configAllowDependingOnPrivateLibs + , configCoverageFor = combine configCoverageFor } where combine = combine' savedConfigureFlags @@ -636,8 +637,6 @@ instance Semigroup SavedConfig where , testKeepTix = combine testKeepTix , testWrapper = combine testWrapper , testFailWhenNoTestSuites = combine testFailWhenNoTestSuites - , testCoverageLibsModules = combine testCoverageLibsModules - , testCoverageDistPrefs = combine testCoverageDistPrefs , testOptions = lastNonEmpty testOptions } where diff --git a/cabal-install/src/Distribution/Client/ProjectBuilding.hs b/cabal-install/src/Distribution/Client/ProjectBuilding.hs index 1b00113d8e7..84ae5da18e8 100644 --- a/cabal-install/src/Distribution/Client/ProjectBuilding.hs +++ b/cabal-install/src/Distribution/Client/ProjectBuilding.hs @@ -1356,6 +1356,7 @@ buildAndInstallUnpackedPackage configureFlags v = flip filterConfigureFlags v $ setupHsConfigureFlags + plan rpkg pkgshared verbosity @@ -1714,6 +1715,7 @@ buildInplaceUnpackedPackage configureFlags v = flip filterConfigureFlags v $ setupHsConfigureFlags + plan rpkg pkgshared verbosity @@ -1734,11 +1736,8 @@ buildInplaceUnpackedPackage testFlags v = flip filterTestFlags v $ setupHsTestFlags - plan pkg - pkgshared verbosity - distDirLayout builddir testArgs _ = setupHsTestArgs pkg diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 1ddff54b7f7..d949437f5d6 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -727,6 +727,7 @@ convertLegacyPerPackageFlags , configDebugInfo = packageConfigDebugInfo , configDumpBuildInfo = packageConfigDumpBuildInfo , configRelocatable = packageConfigRelocatable + , configCoverageFor = _ } = configFlags packageConfigProgramPaths = MapLast (Map.fromList configProgramPaths) packageConfigProgramArgs = MapMappend (Map.fromListWith (++) configProgramArgs) @@ -767,8 +768,6 @@ convertLegacyPerPackageFlags , testKeepTix = packageConfigTestKeepTix , testWrapper = packageConfigTestWrapper , testFailWhenNoTestSuites = packageConfigTestFailWhenNoTestSuites - , testCoverageLibsModules = _ - , testCoverageDistPrefs = _ , testOptions = packageConfigTestTestOptions } = testFlags @@ -1039,6 +1038,7 @@ convertToLegacyAllPackageConfig , configUseResponseFiles = mempty , configDumpBuildInfo = mempty , configAllowDependingOnPrivateLibs = mempty + , configCoverageFor = mempty } haddockFlags = @@ -1115,6 +1115,7 @@ convertToLegacyPerPackageConfig PackageConfig{..} = , configUseResponseFiles = mempty , configDumpBuildInfo = packageConfigDumpBuildInfo , configAllowDependingOnPrivateLibs = mempty + , configCoverageFor = mempty } installFlags = @@ -1162,8 +1163,6 @@ convertToLegacyPerPackageConfig PackageConfig{..} = , testKeepTix = packageConfigTestKeepTix , testWrapper = packageConfigTestWrapper , testFailWhenNoTestSuites = packageConfigTestFailWhenNoTestSuites - , testCoverageLibsModules = mempty - , testCoverageDistPrefs = mempty , testOptions = packageConfigTestTestOptions } diff --git a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs index 5d9c5e9fef1..a13d35011b1 100644 --- a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs +++ b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs @@ -1040,6 +1040,7 @@ printPlan showConfigureFlags elab = let fullConfigureFlags = setupHsConfigureFlags + elaboratedPlan (ReadyPackage elab) elaboratedShared verbosity diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 4417b1eef24..f6346655c4d 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -149,7 +149,6 @@ import Distribution.Simple.Program.Db import Distribution.Simple.Program.Find import Distribution.Simple.Setup ( Flag (..) - , TestFlags (testCoverageDistPrefs) , flagToList , flagToMaybe , fromFlagOrDefault @@ -4085,12 +4084,14 @@ computeInstallDirs storeDirLayout defaultInstallDirs elaboratedShared elab -- make the various Setup.hs {configure,build,copy} flags setupHsConfigureFlags - :: ElaboratedReadyPackage + :: ElaboratedInstallPlan + -> ElaboratedReadyPackage -> ElaboratedSharedConfig -> Verbosity -> FilePath -> Cabal.ConfigFlags setupHsConfigureFlags + plan (ReadyPackage elab@ElaboratedConfiguredPackage{..}) sharedConfig@ElaboratedSharedConfig{..} verbosity @@ -4236,6 +4237,8 @@ setupHsConfigureFlags Just _ -> error "non-library dependency" Nothing -> LMainLibName + configCoverageFor = determineCoverageFor elabPkgSourceId plan + setupHsConfigureArgs :: ElaboratedConfiguredPackage -> [String] @@ -4282,14 +4285,11 @@ setupHsBuildArgs (ElaboratedConfiguredPackage{elabPkgOrComp = ElabComponent _}) [] setupHsTestFlags - :: ElaboratedInstallPlan - -> ElaboratedConfiguredPackage - -> ElaboratedSharedConfig + :: ElaboratedConfiguredPackage -> Verbosity - -> DistDirLayout -> FilePath -> Cabal.TestFlags -setupHsTestFlags plan (ElaboratedConfiguredPackage{..}) sharedConfig verbosity distDirLayout builddir = +setupHsTestFlags (ElaboratedConfiguredPackage{..}) verbosity builddir = Cabal.TestFlags { testDistPref = toFlag builddir , testVerbosity = toFlag verbosity @@ -4299,47 +4299,8 @@ setupHsTestFlags plan (ElaboratedConfiguredPackage{..}) sharedConfig verbosity d , testKeepTix = toFlag elabTestKeepTix , testWrapper = maybe mempty toFlag elabTestWrapper , testFailWhenNoTestSuites = toFlag elabTestFailWhenNoTestSuites - , testCoverageLibsModules = toFlag covIncludeModules - , testCoverageDistPrefs = toFlag covLibsDistPref , testOptions = elabTestTestOptions } - where - -- The path to dist dir of each of the libraries to consider in hpc, from which Cabal determines the path to the `mix` dir. - covLibsDistPref = map (distBuildDirectory distDirLayout . elabDistDirParams sharedConfig) librariesToCover - -- The list of modules from libraries to consider in hpc, that Cabal passes to the hpc markup call - -- This list includes all modules, not only the exposed ones. - covIncludeModules = concatMap (\ElaboratedConfiguredPackage{elabModuleShape = modShape} -> Map.keys $ modShapeProvides modShape) librariesToCover - - -- The list of non-pre-existing libraries without module holes, i.e. the - -- main library and sub-libraries components of all the local packages in - -- the project that do not require instantiations or are instantiations, - -- and the testsuite component. - -- - -- TODO: I think that if the packages it depends on are still uninstalled, - -- this seemingly includes the packages that are not local to the project?! - -- Weird, because we filter on localToProject! - -- Try it on cabal-install: cabal test --enable-coverage cabal-install - librariesToCover = - mapMaybe - ( \case - InstallPlan.Installed elab - | shouldCoverPkg elab -> Just elab - InstallPlan.Configured elab - | shouldCoverPkg elab -> Just elab - _ -> Nothing - ) - $ Graph.toList - $ InstallPlan.toGraph plan - - shouldCoverPkg ElaboratedConfiguredPackage{elabModuleShape = modShape, elabPkgSourceId = pkgId} = - elabLocalToProject - && not (isIndefiniteOrInstantiation modShape) - -- TODO(#9493): We can only cover libraries in the same package - -- as the testsuite - && pkgId == elabPkgSourceId - - isIndefiniteOrInstantiation :: ModuleShape -> Bool - isIndefiniteOrInstantiation = not . Set.null . modShapeRequires setupHsTestArgs :: ElaboratedConfiguredPackage -> [String] -- TODO: Does the issue #3335 affects test as well @@ -4681,3 +4642,37 @@ inplaceBinRoot inplaceBinRoot layout config package = distBuildDirectory layout (elabDistDirParams config package) "build" + +-------------------------------------------------------------------------------- +-- Configure --coverage-for flags + +-- The list of non-pre-existing libraries without module holes, i.e. the +-- main library and sub-libraries components of all the local packages in +-- the project that do not require instantiations or are instantiations. +determineCoverageFor + :: PackageId + -- ^ The 'PackageId' of the package or component being configured + -> ElaboratedInstallPlan + -> Flag [UnitId] +determineCoverageFor configuredPkgSourceId plan = + Flag + $ mapMaybe + ( \case + InstallPlan.Installed elab + | shouldCoverPkg elab -> Just $ elabUnitId elab + InstallPlan.Configured elab + | shouldCoverPkg elab -> Just $ elabUnitId elab + _ -> Nothing + ) + $ Graph.toList + $ InstallPlan.toGraph plan + where + shouldCoverPkg ElaboratedConfiguredPackage{elabModuleShape, elabPkgSourceId, elabLocalToProject} = + elabLocalToProject + && not (isIndefiniteOrInstantiation elabModuleShape) + -- TODO(#9493): We can only cover libraries in the same package + -- as the testsuite + && configuredPkgSourceId == elabPkgSourceId + + isIndefiniteOrInstantiation :: ModuleShape -> Bool + isIndefiniteOrInstantiation = not . Set.null . modShapeRequires diff --git a/cabal-install/src/Distribution/Client/Setup.hs b/cabal-install/src/Distribution/Client/Setup.hs index 5489706ac8e..348d7fcd1e2 100644 --- a/cabal-install/src/Distribution/Client/Setup.hs +++ b/cabal-install/src/Distribution/Client/Setup.hs @@ -802,8 +802,8 @@ filterConfigureFlags flags cabalLibVersion } -- Cabal < 1.10.0 doesn't know about '--disable-tests'. flags_1_10_0 = flags_1_12_0{configTests = NoFlag} - -- Cabal < 1.3.10 does not grok the '--constraints' flag. - flags_1_3_10 = flags_1_10_0{configConstraints = []} + -- Cabal < 1.3.10 does not grok the '--constraints' flag nor --coverage-for. + flags_1_3_10 = flags_1_10_0{configConstraints = [], configCoverageFor = NoFlag} -- | Get the package database settings from 'ConfigFlags', accounting for -- @--package-db@ and @--user@ flags. @@ -1093,24 +1093,18 @@ filterTestFlags :: TestFlags -> Version -> TestFlags filterTestFlags flags cabalLibVersion -- NB: we expect the latest version to be the most common case, -- so test it first. - | cabalLibVersion >= mkVersion [3, 11, 0] = flags_latest + | cabalLibVersion >= mkVersion [3, 0, 0] = flags_latest -- The naming convention is that flags_version gives flags with -- all flags *introduced* in version eliminated. -- It is NOT the latest version of Cabal library that -- these flags work for; version of introduction is a more -- natural metric. - | cabalLibVersion < mkVersion [3, 11, 0] = flags_3_10_0 | cabalLibVersion < mkVersion [3, 0, 0] = flags_3_0_0 | otherwise = error "the impossible just happened" -- see first guard where flags_latest = flags - flags_3_10_0 = - flags_latest - { Cabal.testCoverageLibsModules = NoFlag - , Cabal.testCoverageDistPrefs = NoFlag - } flags_3_0_0 = - flags_3_10_0 + flags_latest { -- Cabal < 3.0 doesn't know about --test-wrapper Cabal.testWrapper = NoFlag }