diff --git a/cabal-install/src/Distribution/Client/DistDirLayout.hs b/cabal-install/src/Distribution/Client/DistDirLayout.hs index 0e8fd2026a5..9dd5e160846 100644 --- a/cabal-install/src/Distribution/Client/DistDirLayout.hs +++ b/cabal-install/src/Distribution/Client/DistDirLayout.hs @@ -87,7 +87,7 @@ data ProjectFileKey = ProjectFileKeyMain | ProjectFileKeyLocal | ProjectFileKeyFreeze - deriving (Eq, Ord, Show) + deriving (Eq, Ord, Show, Bounded, Enum) -- | The layout of the project state directory. Traditionally this has been -- called the @dist@ directory. diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index 603c1e00839..2205d75cd94 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -4,7 +4,7 @@ {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeApplications #-} -{-# OPTIONS_GHC -Wno-unused-matches #-} +{-# LANGUAGE ViewPatterns #-} -- | Handling project configuration. module Distribution.Client.ProjectConfig @@ -59,7 +59,6 @@ module Distribution.Client.ProjectConfig , fetchAndReadSourcePackages -- * Resolving configuration - , lookupLocalPackageConfig , projectConfigWithBuilderRepoContext , projectConfigWithSolverRepoContext , SolverSettings (..) @@ -264,28 +263,6 @@ import Distribution.Solver.Types.ProjectConfigPath -- Resolving configuration to settings -- --- | Look up a 'PackageConfig' field in the 'ProjectConfig' for a specific --- 'PackageName'. This returns the configuration that applies to all local --- packages plus any package-specific configuration for this package. -lookupLocalPackageConfig - :: Monoid a - => (PackageConfig -> a) - -> ProjectConfig - -> PackageName - -> a -lookupLocalPackageConfig - field - ProjectConfig - { projectConfigLocalPackages - , projectConfigSpecificPackage - } - pkgname = - field projectConfigLocalPackages - <> maybe - mempty - field - (Map.lookup pkgname (getMapMappend projectConfigSpecificPackage)) - -- | Use a 'RepoContext' based on the 'BuildTimeSettings'. projectConfigWithBuilderRepoContext :: Verbosity @@ -769,7 +746,7 @@ readProjectConfig -> Flag FilePath -> DistDirLayout -> Rebuild ProjectConfigSkeleton -readProjectConfig verbosity parserOption _ (Flag True) configFileFlag _ = do +readProjectConfig verbosity _parserOption _ (Flag True) configFileFlag _ = do global <- singletonProjectConfigSkeleton <$> readGlobalConfig verbosity configFileFlag return (global <> singletonProjectConfigSkeleton defaultImplicitProjectConfig) readProjectConfig verbosity parserOption httpTransport _ configFileFlag distDirLayout = do @@ -846,31 +823,76 @@ readProjectLocalFreezeConfig verbosity parserOption httpTransport distDirLayout distDirLayout ProjectFileKeyFreeze --- | Reads a named extended (with imports and conditionals) config file in the given project root dir, or returns empty. --- This function is generic and can be used with the legacy or parsec parser, or a combination of both. -readProjectFileSkeletonGen :: Verbosity -> HttpTransport -> DistDirLayout -> ProjectFileKey -> (FilePath -> IO ProjectConfigSkeleton) -> Rebuild ProjectConfigSkeleton +-- | Reads a named extended (with imports and conditionals) config file in the +-- given project root dir, or returns empty. This function is generic and can +-- be used with the legacy or parsec parser, or a combination of both. +readProjectFileSkeletonGen :: Verbosity -> DistDirLayout -> ProjectFileKey -> (FilePath -> IO ProjectConfigSkeleton) -> Rebuild ProjectConfigSkeleton readProjectFileSkeletonGen verbosity - httpTransport - dir - key + DistDirLayout{distProjectFile, distProjectRootDirectory} + key@(distProjectFile -> extensionFile) parseConfig = do exists <- liftIO $ doesFileExist extensionFile if exists then do + liftIO . notice verbosity $ + "Monitoring: " ++ extensionFile ++ " (" ++ makeAbsolute extensionFile ++ ")" monitorFiles [monitorFileHashed extensionFile] pcs <- liftIO $ parseConfig extensionFile - monitorFiles - [ monitorFileHashed (projectConfigPathRoot path) - | (Nothing, path) <- projectSkeletonImports pcs - ] + + -- If its the main project then we have the local imports to monitor. + -- We need to monitor the project and all of its local imports, We + -- can't monitor remote URI imports. + -- + -- We don't allow duplicate import paths but we do allow multiple + -- imports of the same file by different paths so we'll want to take + -- care to only monitor each file once. There should only ever be one + -- root 'cabal.project' file. + -- + -- In the simple case, if 'cabal.project' imports 'importee-1.config', + -- which imports 'importee-2.config', then we get these paths from + -- 'projectSkeletonImports': + -- + -- "importee-2.config" :| ["importee-1.config", "cabal.project"] + -- "importee-1.config" :| ["cabal.project"] + -- "cabal.project" :| [] + -- + -- 'currentProjectConfigPath' gives us the head of the path, an + -- importee or the root project file. + -- + -- It is possible for the main project file to import the .local or + -- .freeze file explicitly. It is also possible for the user to edit + -- the .local or .freeze file to import the main project file. There's + -- nothing stopping the user from setting up these imports. + + -- Each of the main project file, the .local and .freeze file are read + -- separately. We don't want to monitor them twice, so we filter them + -- out. + when (key == ProjectFileKeyMain) $ do + let paths = + [ path + | let projFile = distProjectFile + , path <- + filter (`notElem` [projFile k | k <- filter (/= key) [minBound .. maxBound]]) $ + ordNub + [ p + | (Nothing, currentProjectConfigPath -> p) <- projectSkeletonImports pcs + ] + ] + for_ paths $ \p -> + liftIO . notice verbosity $ + "Monitoring: " ++ p ++ " (" ++ makeAbsolute p ++ ")" + monitorFiles $ monitorFileHashed <$> paths + return pcs else do monitorFiles [monitorNonExistentFile extensionFile] return mempty where - extensionFile = distProjectFile dir key + makeAbsolute f + | isAbsolute f = f + | otherwise = distProjectRootDirectory f -- There are 3 different variants of the project parsing function. -- 1. readProjectFileSkeletonLegacy: always uses the legacy parser @@ -901,24 +923,24 @@ readProjectFileSkeleton option = -- | Read a project file using the legacy parser. readProjectFileSkeletonLegacy :: Verbosity -> HttpTransport -> DistDirLayout -> ProjectFileKey -> Rebuild ProjectConfigSkeleton readProjectFileSkeletonLegacy verbosity httpTransport distDirLayout key = do - readProjectFileSkeletonGen verbosity httpTransport distDirLayout key $ \fp -> do + readProjectFileSkeletonGen verbosity distDirLayout key $ \fp -> do debug verbosity "Reading project file using the legacy parser" - parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout key fp + parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout key >>= liftIO . reportParseResult verbosity (extensionDescription key) fp -- | Read a project file using the parsec parser, but if that fails, it falls back to the legacy parser. readProjectFileSkeletonFallback :: Verbosity -> HttpTransport -> DistDirLayout -> ProjectFileKey -> Rebuild ProjectConfigSkeleton readProjectFileSkeletonFallback verbosity httpTransport distDirLayout key = do - readProjectFileSkeletonGen verbosity httpTransport distDirLayout key $ \fp -> do + readProjectFileSkeletonGen verbosity distDirLayout key $ \fp -> do debug verbosity "Reading project file using the fallback parser" - (res, bs) <- parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout key fp + (res, bs) <- parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout key let (_, pres) = runParseResult res case pres of -- 1. Successful parse with parsec parser, handle the result as normal. Right{} -> liftIO $ reportParseResultParsec verbosity fp bs res -- 2. The parse failed with the parsec parser, fallback to the legacy parser. Left{} -> do - lres <- parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout key fp + lres <- parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout key case lres of -- 3a. The legacy parser worked, but the parsec parser failed! -- Report a warning to the user that this happened. @@ -932,21 +954,21 @@ readProjectFileSkeletonFallback verbosity httpTransport distDirLayout key = do -- | Read a project file using the parsec parser. readProjectFileSkeletonParsec :: Verbosity -> HttpTransport -> DistDirLayout -> ProjectFileKey -> Rebuild ProjectConfigSkeleton readProjectFileSkeletonParsec verbosity httpTransport distDirLayout key = do - readProjectFileSkeletonGen verbosity httpTransport distDirLayout key $ \fp -> do + readProjectFileSkeletonGen verbosity distDirLayout key $ \fp -> do debug verbosity "Reading project file using the parsec parser" - (res, bs) <- parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout key fp + (res, bs) <- parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout key liftIO $ reportParseResultParsec verbosity fp bs res readProjectFileSkeletonCompare :: Verbosity -> HttpTransport -> DistDirLayout -> ProjectFileKey -> Rebuild ProjectConfigSkeleton readProjectFileSkeletonCompare verbosity httpTransport distDirLayout key = do - readProjectFileSkeletonGen verbosity httpTransport distDirLayout key $ \fp -> do + readProjectFileSkeletonGen verbosity distDirLayout key $ \fp -> do debug verbosity "Reading project file using the comparative parser" - (pres, bs) <- parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout key fp - lres <- parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout key fp + (pres, bs) <- parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout key + lres <- parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout key let (_, ppres) = runParseResult pres case (lres, ppres) of -- 1. Both succeed, compare the results - (OldParser.ProjectParseOk lwarns lpcs, Right ppcs) -> do + (OldParser.ProjectParseOk _lwarns lpcs, Right ppcs) -> do unless (lpcs == ppcs) (dieWithException verbosity $ LegacyAndParsecParseResultsDiffer fp (show lpcs) (show ppcs)) liftIO $ reportParseResultParsec verbosity fp bs pres -- 2. The legacy parser failed, but the parsec parser succeeded. @@ -969,7 +991,7 @@ reportParseResultParsec -> BS.ByteString -> Parsec.ParseResult ProjectFileSource a -> IO a -reportParseResultParsec verbosity fpath contents pr = do +reportParseResultParsec verbosity fpath _contents pr = do let (warnings, result) = runParseResult pr case result of Right x -> do @@ -981,21 +1003,23 @@ reportParseResultParsec verbosity fpath contents pr = do dieWithException verbosity $ ProjectConfigParseFailure $ ProjectConfigParseError errors warnings -- | Reads a named extended (with imports and conditionals) config file in the given project root dir, or returns empty. -parseProjectFileSkeletonLegacy :: Verbosity -> HttpTransport -> DistDirLayout -> ProjectFileKey -> FilePath -> IO (OldParser.ProjectParseResult ProjectConfigSkeleton) -parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout key extensionFile = do +parseProjectFileSkeletonLegacy :: Verbosity -> HttpTransport -> DistDirLayout -> ProjectFileKey -> IO (OldParser.ProjectParseResult ProjectConfigSkeleton) +parseProjectFileSkeletonLegacy verbosity httpTransport distDirLayout key = do + let extensionFile = distProjectFile distDirLayout key bs <- BS.readFile extensionFile res <- parseProject extensionFile (distDownloadSrcDirectory distDirLayout) httpTransport verbosity $ ProjectConfigToParse bs case res of x@(OldParser.ProjectParseOk _ skeleton) -> reportDuplicateImports verbosity skeleton >> pure x x@OldParser.ProjectParseFailed{} -> pure x -parseProjectFileSkeletonParsec :: Verbosity -> HttpTransport -> DistDirLayout -> ProjectFileKey -> FilePath -> IO (Parsec.ParseResult ProjectFileSource ProjectConfigSkeleton, BS.ByteString) -parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout key extensionFile = do +parseProjectFileSkeletonParsec :: Verbosity -> HttpTransport -> DistDirLayout -> ProjectFileKey -> IO (Parsec.ParseResult ProjectFileSource ProjectConfigSkeleton, BS.ByteString) +parseProjectFileSkeletonParsec verbosity httpTransport distDirLayout key = do + let extensionFile = distProjectFile distDirLayout key bs <- BS.readFile extensionFile res <- Parsec.parseProject extensionFile (distDownloadSrcDirectory distDirLayout) httpTransport verbosity $ ProjectConfigToParse bs case snd $ runParseResult res of - x@(Right skeleton) -> reportDuplicateImports verbosity skeleton >> pure (res, bs) - x@Left{} -> pure (res, bs) + Right skeleton -> reportDuplicateImports verbosity skeleton >> pure (res, bs) + Left{} -> pure (res, bs) -- | Render the 'ProjectConfig' format. -- diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 3021e5dfdef..6280fe86558 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -5,7 +5,9 @@ {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE ViewPatterns #-} -- | -- /Elaborated: worked out with great care and nicety of detail; executed with great minuteness: elaborate preparations; elaborate care./ @@ -809,6 +811,9 @@ rebuildInstallPlan projectConfig@ProjectConfig { projectConfigShared , projectConfigBuildOnly + , projectConfigAllPackages + , projectConfigLocalPackages + , projectConfigSpecificPackage } (compiler, platform, progdb) localPackages @@ -877,37 +882,19 @@ rebuildInstallPlan solverSettings = resolveSolverSettings projectConfig logMsg message rest = debugNoWrap verbosity message >> rest + perPkgOption = lookupPerPkgOption (const True) projectConfigAllPackages projectConfigLocalPackages (getMapMappend projectConfigSpecificPackage) + -- TODO: "local" misnomer: we should separate + -- builtin/global/inplace/local packages and packages explicitly + -- mentioned in the project. localPackagesEnabledStanzas = Map.fromList - [ (pkgname, stanzas) + [ (pkgname, Map.fromList $ ((TestStanzas,) <$> tests) ++ ((BenchStanzas,) <$> benches)) | pkg <- localPackages - , -- TODO: misnomer: we should separate - -- builtin/global/inplace/local packages - -- and packages explicitly mentioned in the project - -- - let pkgname = pkgSpecifierTarget pkg - testsEnabled = - lookupLocalPackageConfig - packageConfigTests - projectConfig - pkgname - benchmarksEnabled = - lookupLocalPackageConfig - packageConfigBenchmarks - projectConfig - pkgname - isLocal = isJust (shouldBeLocal pkg) - stanzas - | isLocal = - Map.fromList $ - [ (TestStanzas, enabled) - | enabled <- flagToList testsEnabled - ] - ++ [ (BenchStanzas, enabled) - | enabled <- flagToList benchmarksEnabled - ] - | otherwise = Map.fromList [(TestStanzas, False), (BenchStanzas, False)] + , let pkgname = pkgSpecifierTarget pkg + , let (tests, benches) = case shouldBeLocal pkg of + Just (fmap flagToList . perPkgOption -> f) -> (f packageConfigTests, f packageConfigBenchmarks) + Nothing -> ([False], [False]) ] -- Elaborate the solver's install plan to get a fully detailed plan. This @@ -2331,7 +2318,7 @@ elaborateInstallPlan elabHaddockTargets = [] elabBuildHaddocks = - perPkgOptionFlag pkgid False packageConfigDocumentation + perPkgOptionFlag False pkgid packageConfigDocumentation -- `documentation: true` should imply `-haddock` for GHC addHaddockIfDocumentationEnabled :: ConfiguredProgram -> ConfiguredProgram @@ -2379,37 +2366,37 @@ elaborateInstallPlan -- 'elabBuildOptions' accurately reflects what will actually be built. elabBuildOptionsRaw = LBC.BuildOptions - { withVanillaLib = perPkgOptionFlag pkgid True packageConfigVanillaLib -- TODO: [required feature]: also needs to be handled recursively + { withVanillaLib = perPkgOptionFlag True pkgid packageConfigVanillaLib -- TODO: [required feature]: also needs to be handled recursively , withSharedLib = canBuildSharedLibs && pkgid `Set.member` pkgsUseSharedLibrary - , withStaticLib = perPkgOptionFlag pkgid False packageConfigStaticLib + , withStaticLib = perPkgOptionFlag False pkgid packageConfigStaticLib , withDynExe = - perPkgOptionFlag pkgid False packageConfigDynExe + perPkgOptionFlag False pkgid packageConfigDynExe -- We can't produce a dynamic executable if the user -- wants to enable executable profiling but the -- compiler doesn't support prof+dyn. && (okProfDyn || not profExe) - , withFullyStaticExe = perPkgOptionFlag pkgid False packageConfigFullyStaticExe - , withGHCiLib = perPkgOptionFlag pkgid False packageConfigGHCiLib -- TODO: [required feature] needs to default to enabled on windows still + , withFullyStaticExe = perPkgOptionFlag False pkgid packageConfigFullyStaticExe + , withGHCiLib = perPkgOptionFlag False pkgid packageConfigGHCiLib -- TODO: [required feature] needs to default to enabled on windows still , withProfExe = profExe , withProfLib = canBuildProfilingLibs && pkgid `Set.member` pkgsUseProfilingLibrary , withProfLibShared = canBuildProfilingSharedLibs && pkgid `Set.member` pkgsUseProfilingLibraryShared - , withBytecodeLib = perPkgOptionFlag pkgid False packageConfigBytecodeLib - , exeCoverage = perPkgOptionFlag pkgid False packageConfigCoverage - , libCoverage = perPkgOptionFlag pkgid False packageConfigCoverage - , withOptimization = perPkgOptionFlag pkgid NormalOptimisation packageConfigOptimization - , splitObjs = perPkgOptionFlag pkgid False packageConfigSplitObjs - , splitSections = perPkgOptionFlag pkgid False packageConfigSplitSections - , stripLibs = perPkgOptionFlag pkgid False packageConfigStripLibs - , stripExes = perPkgOptionFlag pkgid False packageConfigStripExes - , withDebugInfo = perPkgOptionFlag pkgid NoDebugInfo packageConfigDebugInfo - , relocatable = perPkgOptionFlag pkgid False packageConfigRelocatable + , withBytecodeLib = perPkgOptionFlag False pkgid packageConfigBytecodeLib + , exeCoverage = perPkgOptionFlag False pkgid packageConfigCoverage + , libCoverage = perPkgOptionFlag False pkgid packageConfigCoverage + , withOptimization = perPkgOptionFlag NormalOptimisation pkgid packageConfigOptimization + , splitObjs = perPkgOptionFlag False pkgid packageConfigSplitObjs + , splitSections = perPkgOptionFlag False pkgid packageConfigSplitSections + , stripLibs = perPkgOptionFlag False pkgid packageConfigStripLibs + , stripExes = perPkgOptionFlag False pkgid packageConfigStripExes + , withDebugInfo = perPkgOptionFlag NoDebugInfo pkgid packageConfigDebugInfo + , relocatable = perPkgOptionFlag False pkgid packageConfigRelocatable , withProfLibDetail = elabProfExeDetail , withProfExeDetail = elabProfLibDetail , programPrefix = elabProgPrefix , programSuffix = elabProgSuffix } okProfDyn = profilingDynamicSupportedOrUnknown compiler - profExe = perPkgOptionFlag pkgid False packageConfigProf + profExe = perPkgOptionFlag False pkgid packageConfigProf elabBuildOptions = Cabal.adjustBuildOptions compiler compilerProgDb elabBuildOptionsRaw @@ -2417,12 +2404,12 @@ elaborateInstallPlan , elabProfLibDetail ) = perPkgOptionLibExeFlag - pkgid ProfDetailDefault + pkgid packageConfigProfDetail packageConfigProfLibDetail - elabDumpBuildInfo = perPkgOptionFlag pkgid NoDumpBuildInfo packageConfigDumpBuildInfo + elabDumpBuildInfo = perPkgOptionFlag NoDumpBuildInfo pkgid packageConfigDumpBuildInfo -- Combine the configured compiler prog settings with the user-supplied -- config. For the compiler progs any user-supplied config was taken @@ -2434,7 +2421,8 @@ elaborateInstallPlan [ (programId prog, programPath prog) | prog <- configuredPrograms compilerProgDb ] - <> perPkgOptionMapLast pkgid packageConfigProgramPaths + <> getMapLast (perPkgOption pkgid packageConfigProgramPaths) + elabProgramArgs = -- Workaround for -- @@ -2459,8 +2447,9 @@ elaborateInstallPlan , not (null args) ] ) - (perPkgOptionMapMappend pkgid packageConfigProgramArgs) - elabProgramPathExtra = perPkgOptionNubList pkgid packageConfigProgramPathExtra + (getMapMappend $ perPkgOption pkgid packageConfigProgramArgs) + + elabProgramPathExtra = fromNubList $ perPkgOption pkgid packageConfigProgramPathExtra elabConfiguredPrograms = configuredPrograms compilerProgDb elabConfigureScriptArgs = perPkgOptionList pkgid packageConfigConfigureArgs elabExtraLibDirs = perPkgOptionList pkgid packageConfigExtraLibDirs @@ -2470,73 +2459,51 @@ elaborateInstallPlan elabProgPrefix = perPkgOptionMaybe pkgid packageConfigProgPrefix elabProgSuffix = perPkgOptionMaybe pkgid packageConfigProgSuffix - elabHaddockHoogle = perPkgOptionFlag pkgid False packageConfigHaddockHoogle - elabHaddockHtml = perPkgOptionFlag pkgid False packageConfigHaddockHtml + elabHaddockHoogle = perPkgOptionFlag False pkgid packageConfigHaddockHoogle + elabHaddockHtml = perPkgOptionFlag False pkgid packageConfigHaddockHtml elabHaddockHtmlLocation = perPkgOptionMaybe pkgid packageConfigHaddockHtmlLocation - elabHaddockForeignLibs = perPkgOptionFlag pkgid False packageConfigHaddockForeignLibs - elabHaddockForHackage = perPkgOptionFlag pkgid Cabal.ForDevelopment packageConfigHaddockForHackage - elabHaddockExecutables = perPkgOptionFlag pkgid False packageConfigHaddockExecutables - elabHaddockTestSuites = perPkgOptionFlag pkgid False packageConfigHaddockTestSuites - elabHaddockBenchmarks = perPkgOptionFlag pkgid False packageConfigHaddockBenchmarks - elabHaddockInternal = perPkgOptionFlag pkgid False packageConfigHaddockInternal + elabHaddockForeignLibs = perPkgOptionFlag False pkgid packageConfigHaddockForeignLibs + elabHaddockForHackage = perPkgOptionFlag Cabal.ForDevelopment pkgid packageConfigHaddockForHackage + elabHaddockExecutables = perPkgOptionFlag False pkgid packageConfigHaddockExecutables + elabHaddockTestSuites = perPkgOptionFlag False pkgid packageConfigHaddockTestSuites + elabHaddockBenchmarks = perPkgOptionFlag False pkgid packageConfigHaddockBenchmarks + elabHaddockInternal = perPkgOptionFlag False pkgid packageConfigHaddockInternal elabHaddockCss = perPkgOptionMaybe pkgid packageConfigHaddockCss - elabHaddockLinkedSource = perPkgOptionFlag pkgid False packageConfigHaddockLinkedSource - elabHaddockQuickJump = perPkgOptionFlag pkgid False packageConfigHaddockQuickJump + elabHaddockLinkedSource = perPkgOptionFlag False pkgid packageConfigHaddockLinkedSource + elabHaddockQuickJump = perPkgOptionFlag False pkgid packageConfigHaddockQuickJump elabHaddockHscolourCss = perPkgOptionMaybe pkgid packageConfigHaddockHscolourCss elabHaddockContents = perPkgOptionMaybe pkgid packageConfigHaddockContents elabHaddockIndex = perPkgOptionMaybe pkgid packageConfigHaddockIndex elabHaddockBaseUrl = perPkgOptionMaybe pkgid packageConfigHaddockBaseUrl elabHaddockResourcesDir = perPkgOptionMaybe pkgid packageConfigHaddockResourcesDir elabHaddockOutputDir = perPkgOptionMaybe pkgid packageConfigHaddockOutputDir - elabHaddockUseUnicode = perPkgOptionFlag pkgid False packageConfigHaddockUseUnicode + elabHaddockUseUnicode = perPkgOptionFlag False pkgid packageConfigHaddockUseUnicode elabTestMachineLog = perPkgOptionMaybe pkgid packageConfigTestMachineLog elabTestHumanLog = perPkgOptionMaybe pkgid packageConfigTestHumanLog elabTestShowDetails = perPkgOptionMaybe pkgid packageConfigTestShowDetails - elabTestKeepTix = perPkgOptionFlag pkgid False packageConfigTestKeepTix + elabTestKeepTix = perPkgOptionFlag False pkgid packageConfigTestKeepTix elabTestWrapper = perPkgOptionMaybe pkgid packageConfigTestWrapper - elabTestFailWhenNoTestSuites = perPkgOptionFlag pkgid False packageConfigTestFailWhenNoTestSuites + elabTestFailWhenNoTestSuites = perPkgOptionFlag False pkgid packageConfigTestFailWhenNoTestSuites elabTestTestOptions = perPkgOptionList pkgid packageConfigTestTestOptions elabBenchmarkOptions = perPkgOptionList pkgid packageConfigBenchmarkOptions - perPkgOptionFlag :: PackageId -> a -> (PackageConfig -> Flag a) -> a + perPkgOptionFlag :: a -> PackageId -> (PackageConfig -> Flag a) -> a + perPkgOptionFlag def = fmap (fromFlagOrDefault def) . perPkgOption + perPkgOptionMaybe :: PackageId -> (PackageConfig -> Flag a) -> Maybe a + perPkgOptionMaybe = fmap flagToMaybe . perPkgOption + perPkgOptionList :: PackageId -> (PackageConfig -> [a]) -> [a] + perPkgOptionList = perPkgOption - perPkgOptionFlag pkgid def f = fromFlagOrDefault def (lookupPerPkgOption pkgid f) - perPkgOptionMaybe pkgid f = flagToMaybe (lookupPerPkgOption pkgid f) - perPkgOptionList pkgid f = lookupPerPkgOption pkgid f - perPkgOptionNubList pkgid f = fromNubList (lookupPerPkgOption pkgid f) - perPkgOptionMapLast pkgid f = getMapLast (lookupPerPkgOption pkgid f) - perPkgOptionMapMappend pkgid f = getMapMappend (lookupPerPkgOption pkgid f) + perPkgOptionLibExeFlag :: a -> PackageId -> (PackageConfig -> Flag a) -> (PackageConfig -> Flag a) -> (a, a) + perPkgOptionLibExeFlag (fromFlagOrDefault -> f) pkgid (perPkgOption pkgid -> both) (perPkgOption pkgid -> lib) = + (f both, f (both <> lib)) - perPkgOptionLibExeFlag pkgid def fboth flib = (exe, lib) - where - exe = fromFlagOrDefault def bothflag - lib = fromFlagOrDefault def (bothflag <> libflag) - - bothflag = lookupPerPkgOption pkgid fboth - libflag = lookupPerPkgOption pkgid flib - - lookupPerPkgOption - :: (Package pkg, Monoid m) - => pkg - -> (PackageConfig -> m) - -> m - lookupPerPkgOption pkg f = - -- This is where we merge the options from the project config that - -- apply to all packages, all project local packages, and to specific - -- named packages - global <> local <> perpkg - where - global = f allPackagesConfig - local - | isLocalToProject pkg = - f localPackagesConfig - | otherwise = - mempty - perpkg = maybe mempty f (Map.lookup (packageName pkg) perPackageConfig) + perPkgOption :: (Package pkg, Monoid m) => pkg -> (PackageConfig -> m) -> m + perPkgOption = lookupPerPkgOption isLocalToProject allPackagesConfig localPackagesConfig perPackageConfig inplacePackageDbs = corePackageDbs @@ -2652,8 +2619,8 @@ elaborateInstallPlan fromFlagOrDefault compilerShouldUseProfilingLibByDefault (profBothFlag <> profLibFlag) where pkgid = packageId pkg - profBothFlag = lookupPerPkgOption pkgid packageConfigProf - profLibFlag = lookupPerPkgOption pkgid packageConfigProfLib + profBothFlag = perPkgOption pkgid packageConfigProf + profLibFlag = perPkgOption pkgid packageConfigProfLib pkgsUseProfilingLibraryShared :: Set PackageId pkgsUseProfilingLibraryShared = @@ -4693,3 +4660,23 @@ determineCoverageFor configuredPkg plan = isIndefiniteOrInstantiation :: ModuleShape -> Bool isIndefiniteOrInstantiation = not . Set.null . modShapeRequires + +-- | Look up and merge the options from the project config that apply to all +-- packages, all project local packages, and to specific named packages. +lookupPerPkgOption + :: (Package pkg, Monoid m) + => (pkg -> Bool) + -> PackageConfig + -> PackageConfig + -> Map PackageName PackageConfig + -> pkg + -> (PackageConfig -> m) + -> m +lookupPerPkgOption isLocalPkg allPackagesConfig localPackagesConfig perPackageConfig pkg f = + global `mappend` local `mappend` perpkg + where + global = f allPackagesConfig + local + | isLocalPkg pkg = f localPackagesConfig + | otherwise = mempty + perpkg = maybe mempty f (Map.lookup (packageName pkg) perPackageConfig) diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/app/Main.hs b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/app/Main.hs new file mode 100644 index 00000000000..f16f6f53396 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/app/Main.hs @@ -0,0 +1,4 @@ +module Main (main) where + +main :: IO () +main = putStrLn "Hello, Haskell!" diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal-project-repro.cabal b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal-project-repro.cabal new file mode 100644 index 00000000000..28188f08ec1 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal-project-repro.cabal @@ -0,0 +1,25 @@ +cabal-version: 3.0 +name: cabal-project-repro +version: 0.1.0.0 +license: BSD-3-Clause +author: Julian Ospald +maintainer: hasufell@posteo.de +build-type: Simple + +common warnings + ghc-options: -Wall + +executable filemonitor-test + import: warnings + main-is: Main.hs + build-depends: base, filepath + hs-source-dirs: app + default-language: Haskell2010 + +test-suite cabal-project-repro-test + import: warnings + default-language: Haskell2010 + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Main.hs + build-depends: base diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.freeze-only.out b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.freeze-only.out new file mode 100644 index 00000000000..3dc2dfa5dc6 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.freeze-only.out @@ -0,0 +1,45 @@ +# Disabling tests on the command line +# cabal build +Monitoring: /cabal.freeze-only.project (/cabal.freeze-only.project) +Monitoring: /cabal.freeze-only.project.freeze (/cabal.freeze-only.project.freeze) +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (first run) +Configuring executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Preprocessing executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Building executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +# Enabling tests on the command line +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (test:cabal-project-repro-test) (first run) + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (configuration changed) +Configuring test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Preprocessing test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Building test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Error: [Cabal-7125] +Failed to build cabal-project-repro-0.1.0.0-inplace-cabal-project-repro-test. +# As-is, the project should not build +# cabal build +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (test:cabal-project-repro-test) (first run) + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (configuration changed) +Preprocessing test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Building test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Error: [Cabal-7125] +Failed to build cabal-project-repro-0.1.0.0-inplace-cabal-project-repro-test. +# Rewriting an imported project file to disable tests +# cabal clean +# cabal build +Monitoring: /cabal.freeze-only.project (/cabal.freeze-only.project) +Monitoring: /cabal.freeze-only.project.freeze (/cabal.freeze-only.project.freeze) +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (first run) +Configuring executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Preprocessing executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Building executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.freeze-only.project b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.freeze-only.project new file mode 100644 index 00000000000..d2a7aaa3993 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.freeze-only.project @@ -0,0 +1,2 @@ +-- NOTE: Intentionally empty as we can't specify +-- --project-file=cabal.freeze-only.project when that file doesn't exist. diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.freeze-only.project.freeze b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.freeze-only.project.freeze new file mode 100644 index 00000000000..469e652adaf --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.freeze-only.project.freeze @@ -0,0 +1,6 @@ +packages: ./cabal-project-repro.cabal + +package * + Tests: True + +import: nested/hop.config diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.local-only.out b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.local-only.out new file mode 100644 index 00000000000..99fddcdbf80 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.local-only.out @@ -0,0 +1,45 @@ +# Disabling tests on the command line +# cabal build +Monitoring: /cabal.local-only.project (/cabal.local-only.project) +Monitoring: /cabal.local-only.project.local (/cabal.local-only.project.local) +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (first run) +Configuring executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Preprocessing executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Building executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +# Enabling tests on the command line +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (test:cabal-project-repro-test) (first run) + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (configuration changed) +Configuring test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Preprocessing test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Building test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Error: [Cabal-7125] +Failed to build cabal-project-repro-0.1.0.0-inplace-cabal-project-repro-test. +# As-is, the project should not build +# cabal build +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (test:cabal-project-repro-test) (first run) + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (configuration changed) +Preprocessing test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Building test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Error: [Cabal-7125] +Failed to build cabal-project-repro-0.1.0.0-inplace-cabal-project-repro-test. +# Rewriting an imported project file to disable tests +# cabal clean +# cabal build +Monitoring: /cabal.local-only.project (/cabal.local-only.project) +Monitoring: /cabal.local-only.project.local (/cabal.local-only.project.local) +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (first run) +Configuring executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Preprocessing executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Building executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.local-only.project b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.local-only.project new file mode 100644 index 00000000000..84b597f1aae --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.local-only.project @@ -0,0 +1,2 @@ +-- NOTE: Intentionally empty as we can't specify +-- --project-file=cabal.local-only.project when that file doesn't exist. diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.local-only.project.local b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.local-only.project.local new file mode 100644 index 00000000000..469e652adaf --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.local-only.project.local @@ -0,0 +1,6 @@ +packages: ./cabal-project-repro.cabal + +package * + Tests: True + +import: nested/hop.config diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.main-project.out b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.main-project.out new file mode 100644 index 00000000000..27fca4d01f9 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.main-project.out @@ -0,0 +1,87 @@ +# Disabling tests on the command line +# cabal build +Monitoring: /cabal.project (/cabal.project) +Monitoring: nested/hop.config (/nested/hop.config) +Monitoring: nested/deeply-nested/hop.config (/nested/deeply-nested/hop.config) +Monitoring: test/tests-toggle.config (/test/tests-toggle.config) +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (first run) +Configuring executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Preprocessing executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Building executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +# Enabling tests on the command line +# cabal build +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (test:cabal-project-repro-test) (first run) + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (configuration changed) +Configuring test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Preprocessing test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Building test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Error: [Cabal-7125] +Failed to build cabal-project-repro-0.1.0.0-inplace-cabal-project-repro-test. +# Checking for the existence of: /cabal.project.local. It was not found. +# Disabling tests with configure command +# cabal configure +# cabal build +Monitoring: /cabal.project (/cabal.project) +Monitoring: nested/hop.config (/nested/hop.config) +Monitoring: nested/deeply-nested/hop.config (/nested/deeply-nested/hop.config) +Monitoring: test/tests-toggle.config (/test/tests-toggle.config) +Monitoring: /cabal.project.local (/cabal.project.local) +Warning: The builddir option is not supported in project and config files. It will be ignored. +Resolving dependencies... +Up to date +# Enabling tests on the command line +# cabal configure +Monitoring: /cabal.project (/cabal.project) +Monitoring: nested/hop.config (/nested/hop.config) +Monitoring: nested/deeply-nested/hop.config (/nested/deeply-nested/hop.config) +Monitoring: test/tests-toggle.config (/test/tests-toggle.config) +# cabal build +Monitoring: /cabal.project (/cabal.project) +Monitoring: nested/hop.config (/nested/hop.config) +Monitoring: nested/deeply-nested/hop.config (/nested/deeply-nested/hop.config) +Monitoring: test/tests-toggle.config (/test/tests-toggle.config) +Monitoring: /cabal.project.local (/cabal.project.local) +Warning: The builddir option is not supported in project and config files. It will be ignored. +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (test:cabal-project-repro-test) (first run) + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (configuration changed) +Preprocessing test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Building test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Error: [Cabal-7125] +Failed to build cabal-project-repro-0.1.0.0-inplace-cabal-project-repro-test. +# As-is, the project should not build +# cabal build +Monitoring: /cabal.project (/cabal.project) +Monitoring: nested/hop.config (/nested/hop.config) +Monitoring: nested/deeply-nested/hop.config (/nested/deeply-nested/hop.config) +Monitoring: test/tests-toggle.config (/test/tests-toggle.config) +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (test:cabal-project-repro-test) (first run) + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (configuration changed) +Preprocessing test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Building test suite 'cabal-project-repro-test' for cabal-project-repro-0.1.0.0... +Error: [Cabal-7125] +Failed to build cabal-project-repro-0.1.0.0-inplace-cabal-project-repro-test. +# Rewriting an imported project file to disable tests +# cabal clean +# cabal build +Monitoring: /cabal.project (/cabal.project) +Monitoring: nested/hop.config (/nested/hop.config) +Monitoring: nested/deeply-nested/hop.config (/nested/deeply-nested/hop.config) +Monitoring: test/tests-toggle.config (/test/tests-toggle.config) +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - cabal-project-repro-0.1.0.0 (exe:filemonitor-test) (first run) +Configuring executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Preprocessing executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... +Building executable 'filemonitor-test' for cabal-project-repro-0.1.0.0... diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.project b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.project new file mode 100644 index 00000000000..469e652adaf --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.project @@ -0,0 +1,6 @@ +packages: ./cabal-project-repro.cabal + +package * + Tests: True + +import: nested/hop.config diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.test.hs b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.test.hs new file mode 100644 index 00000000000..60ee3615e76 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/cabal.test.hs @@ -0,0 +1,108 @@ +import Prelude hiding (log) +import Test.Cabal.Prelude +import System.Exit (ExitCode(ExitFailure)) +import System.IO +import System.Directory (doesFileExist, removeFile) +import Control.Monad.Trans.Reader + +-- If tests are enabled then we get this output: +-- [1 of 1] Compiling Main +-- test/Main.hs:4:8: error: [GHC-88464] +-- Variable not in scope: puStrLn :: [Char] -> IO () +-- Suggested fix: Perhaps use ‘putStrLn’ (imported from Prelude) +-- | +-- 4 | main = puStrLn "Test suite not yet implemented." +-- | +main = do + cabalTest' "main-project" . recordMode RecordMarked $ do + let opts = ["--project-file=cabal.project"] + runCommandTest opts + runConfigureTest opts + runProjectTest opts + cabalTest' "local-only" . recordMode RecordMarked $ do + let opts = ["--project-file=cabal.local-only.project"] + runCommandTest opts + runProjectTest opts + cabalTest' "freeze-only" . recordMode RecordMarked $ do + let opts = ["--project-file=cabal.freeze-only.project"] + runCommandTest opts + runProjectTest opts + +testNotYetImplementedMsg :: String +testNotYetImplementedMsg = "Test suite not yet implemented" + +failureMsg :: String +failureMsg = "Failed to build cabal-project-repro-0.1.0.0-inplace-cabal-project-repro-test." + +log :: String -> ReaderT TestEnv IO () +log = recordHeader . pure + +-- | Run the build command, with tests disabled then enabled on the command +-- line. +runCommandTest :: [String] -> ReaderT TestEnv IO () +runCommandTest projOpts = do + log "Disabling tests on the command line" + cmdDisabledTests <- cabal' "build" (projOpts ++ ["--disable-tests"]) + assertOutputDoesNotContain testNotYetImplementedMsg cmdDisabledTests + + log "Enabling tests on the command line" + cmdEnabledTests <- fails $ cabal' "build" (projOpts ++ ["--enable-tests"]) + assertOutputContains testNotYetImplementedMsg cmdEnabledTests + +-- | Run the build command, after configuring for tests to be disabled then for +-- tests to be enabled. The project .local is the place where this configuration +-- is saved. The .local file that the configure command creates is deleted at +-- the conclusion of this test. +runConfigureTest :: [String] -> ReaderT TestEnv IO () +runConfigureTest projOpts = do + cwd <- testCurrentDir <$> getTestEnv + let localFile = cwd "cabal.project.local" + haveLocal <- liftIO $ doesFileExist localFile + let existsMsg = if haveLocal then "It exists." else "It was not found." + log $ "Checking for the existence of: " ++ localFile ++ ". " ++ existsMsg + when haveLocal . liftIO $ removeFile localFile + + log "Disabling tests with configure command" + -- The `configure` command will create a .local file disabling tests. + _ <- cabal' "configure" (projOpts ++ ["--disable-tests"]) + haveLocal <- liftIO $ doesFileExist localFile + unless haveLocal $ assertFailure "Was not able to find .local project file after configure command" + assertFileDoesContain localFile "tests: False" + + cmdDisabledTests <- cabal' "build" projOpts + assertOutputDoesNotContain testNotYetImplementedMsg cmdDisabledTests + -- Revert the change, delete the .local file `configure` adds. + liftIO $ removeFile localFile + + log "Enabling tests on the command line" + -- The `configure` command will create a .local file enabbling tests. + _ <- cabal' "configure" (projOpts ++ ["--enable-tests"]) + haveLocal <- liftIO $ doesFileExist localFile + unless haveLocal $ assertFailure "Was not able to find .local project file after configure command" + assertFileDoesContain localFile "tests: True" + + cmdEnabledTests <- fails $ cabal' "build" (projOpts ++ ["--enable-tests"]) + assertOutputContains testNotYetImplementedMsg cmdEnabledTests + -- Revert the change, delete the .local file `configure` adds. + liftIO $ removeFile localFile + +-- | Runs the build command that should fail but then a write to an imported +-- project configuration file should be noticed by file monitoring. +runProjectTest :: [String] -> ReaderT TestEnv IO () +runProjectTest projOpts = do + log "As-is, the project should not build" + projEnabledTests <- fails $ cabal' "build" projOpts + assertExitCode (ExitFailure 1) projEnabledTests + assertOutputContains failureMsg projEnabledTests + + -- Change the imported project file with "tests: False". + log "Rewriting an imported project file to disable tests" + cwd <- testCurrentDir <$> getTestEnv + let configFile = cwd "test" "tests-toggle.config" + liftIO $ writeFile configFile "package *\n tests: False" + _ <- cabal' "clean" projOpts + projDisabledTests <- cabal' "build" projOpts + assertOutputDoesNotContain testNotYetImplementedMsg projDisabledTests + assertOutputDoesNotContain failureMsg projDisabledTests + -- Revert the change. + liftIO $ writeFile configFile "package *\n tests: True" diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/nested/deeply-nested/hop.config b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/nested/deeply-nested/hop.config new file mode 100644 index 00000000000..14a44ec1b90 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/nested/deeply-nested/hop.config @@ -0,0 +1 @@ +import: ../../test/tests-toggle.config diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/nested/hop.config b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/nested/hop.config new file mode 100644 index 00000000000..5023e425930 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/nested/hop.config @@ -0,0 +1,2 @@ +import: deeply-nested/hop.config + diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/test/Main.hs b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/test/Main.hs new file mode 100644 index 00000000000..7e9a17db22b --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/test/Main.hs @@ -0,0 +1,4 @@ +module Main (main) where + +main :: IO () +main = puStrLn "Test suite not yet implemented." diff --git a/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/test/tests-toggle.config b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/test/tests-toggle.config new file mode 100644 index 00000000000..653cd6b9660 --- /dev/null +++ b/cabal-testsuite/PackageTests/ProjectImport/FileMonitoring/test/tests-toggle.config @@ -0,0 +1,2 @@ +package * + Tests: True diff --git a/changelog.d/11884.md b/changelog.d/11884.md new file mode 100644 index 00000000000..82ab94bcf21 --- /dev/null +++ b/changelog.d/11884.md @@ -0,0 +1,12 @@ +--- +synopsis: Fix project file monitoring +packages: [cabal-install] +prs: 11884 +issues: 11567 +--- + +Watch for changes in all project files, the root `cabal.project` and all local +files it imports. We don't monitor remote URI imports. + +Remove `lookupLocalPackageConfig`, an export from module +`Distribution.Client.ProjectConfig`.