From 4dd4975209f372410c25b26cab0c5f2c513164c3 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Mon, 4 Oct 2021 18:23:01 -0400 Subject: [PATCH 01/22] initial parser pass --- .../Client/ProjectConfig/Legacy.hs | 56 +++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 38852d0fd00..84d6e4455c2 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -43,10 +43,11 @@ import Distribution.Solver.Types.ConstraintSource import Distribution.FieldGrammar import Distribution.Package import Distribution.Types.SourceRepo (RepoType) +import Distribution.Types.CondTree (CondTree (..), CondBranch (..), condIfThen, condIfThenElse) import Distribution.PackageDescription - ( dispFlagAssignment ) + ( dispFlagAssignment, Condition (..), ConfVar (..), FlagAssignment (..) ) import Distribution.Simple.Compiler - ( OptimisationLevel(..), DebugInfoLevel(..) ) + ( OptimisationLevel(..), DebugInfoLevel(..), CompilerInfo(..) ) import Distribution.Simple.InstallDirs ( CopyDest (NoCopyDest) ) import Distribution.Simple.Setup ( Flag(Flag), toFlag, fromFlagOrDefault @@ -92,17 +93,36 @@ import Distribution.Simple.Command , OptionField, option, reqArg' ) import Distribution.Types.PackageVersionConstraint ( PackageVersionConstraint ) -import Distribution.Parsec (ParsecParser) +import Distribution.Parsec (ParsecParser, zeroPos) +import Distribution.System (Platform (..)) import qualified Data.Map as Map -import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as BS import Network.URI (URI (..)) +import Distribution.Fields.ConfVar (parseConditionConfVar) +import qualified Distribution.Fields.ParseResult as FPR +import Distribution.Fields.Field (SectionArg (..)) + + ------------------------------------------------------------------ -- Representing the project config file in terms of legacy types -- +-- ProjectConfigSkeleton is a tree of conditional blocks and imports wrapping a legacy config. It can be finalized by providing the conditional resolution invo +-- and then resolving and downloading the imports + +type ProjectConfigImport = String + +type ProjectConfigSkeleton = CondTree ConfVar [ProjectConfigImport] LegacyProjectConfig + +finalizeProjectConfigSkeleton :: Platform -> CompilerInfo -> FlagAssignment -> ProjectConfigSkeleton -> IO LegacyProjectConfig +finalizeProjectConfigSkeleton = undefined + + + + -- | We already have parsers\/pretty-printers for almost all the fields in the -- project config file, but they're in terms of the types used for the command -- line flags for Setup.hs or cabal commands. We don't want to redefine them @@ -854,6 +874,34 @@ convertToLegacyPerPackageConfig PackageConfig {..} = -- Parsing and showing the project config file -- + +parseLegacyProjectSkeleton :: FilePath -> BS.ByteString -> ParseResult ProjectConfigSkeleton +parseLegacyProjectSkeleton source bs = _ . packResult . mconcat . go $ BS.lines bs + where + go :: [BS.ByteString] -> [([CondBranch ConfVar [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString])] + go (l:ls) + | Just condition <- parseCond l = + let (clause, rest) = splitTillIndented ls + in case rest of + (r:rs) | (BS.pack "else") `BS.isPrefixOf` r -> -- TODO handle elif + let (elseClause, lastRest) = splitTillIndented rs + in ([condIfThenElse condition (clauseToNode clause) (clauseToNode elseClause)], [], []) : go lastRest + _ -> ([condIfThen condition (clauseToNode clause)], [], []) : go rest + | Just imp <- parseImport l = ([], [imp], []) : go ls + | otherwise = ([], [], [l]) : go ls + packResult :: ([CondBranch ConfVar [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString]) -> CondTree ConfVar [ProjectConfigImport] [BS.ByteString] + packResult (branches, imps, ls) = CondNode ls imps branches + splitTillIndented = span ((BS.pack " ") `BS.isPrefixOf`) + clauseToNode ls = CondNode ls [] [] -- TODO extract imports from lines + parseCond :: BS.ByteString -> Maybe (Condition ConfVar) + parseCond l | (BS.pack "if(") `BS.isPrefixOf` l = case FPR.runParseResult (parseConditionConfVar [SecArgOther zeroPos (BS.drop 3 l)]) of -- todo drop end also + (_, Left _) -> Nothing + (_, Right x) -> Just x + + | otherwise = Nothing + parseImport l | (BS.pack "import ") `BS.isPrefixOf` l = Just . BS.unpack $ BS.drop (length "import ") l + | otherwise = Nothing + parseLegacyProjectConfig :: FilePath -> BS.ByteString -> ParseResult LegacyProjectConfig parseLegacyProjectConfig source = parseConfig (legacyProjectConfigFieldDescrs constraintSrc) From 2ccd991a7fc516b84c6b9cd0d8d61d20d8928a8c Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Thu, 28 Oct 2021 15:13:19 -0400 Subject: [PATCH 02/22] first compiling pass --- .../PackageDescription/Configuration.hs | 1 + .../src/Distribution/Client/CmdConfigure.hs | 3 +- .../src/Distribution/Client/CmdOutdated.hs | 2 +- .../src/Distribution/Client/ProjectConfig.hs | 37 ++--- .../Client/ProjectConfig/Legacy.hs | 156 +++++++++++++----- .../Distribution/Client/ProjectPlanning.hs | 73 +++++++- .../src/Distribution/Deprecated/ParseUtils.hs | 8 + 7 files changed, 214 insertions(+), 66 deletions(-) diff --git a/Cabal/src/Distribution/PackageDescription/Configuration.hs b/Cabal/src/Distribution/PackageDescription/Configuration.hs index fdb621add6b..e5b40e30189 100644 --- a/Cabal/src/Distribution/PackageDescription/Configuration.hs +++ b/Cabal/src/Distribution/PackageDescription/Configuration.hs @@ -32,6 +32,7 @@ module Distribution.PackageDescription.Configuration ( transformAllBuildInfos, transformAllBuildDepends, transformAllBuildDependsN, + simplifyWithSysParams ) where import Distribution.Compat.Prelude diff --git a/cabal-install/src/Distribution/Client/CmdConfigure.hs b/cabal-install/src/Distribution/Client/CmdConfigure.hs index a7a87faab9b..372b8eb1a32 100644 --- a/cabal-install/src/Distribution/Client/CmdConfigure.hs +++ b/cabal-install/src/Distribution/Client/CmdConfigure.hs @@ -36,6 +36,7 @@ import Distribution.Client.DistDirLayout ( DistDirLayout(..) ) import Distribution.Client.RebuildMonad (runRebuild) import Distribution.Client.ProjectConfig.Types +import Distribution.Types.CondTree (ignoreConditions) configureCommand :: CommandUI (NixStyleFlags ()) configureCommand = CommandUI { @@ -128,7 +129,7 @@ configureAction' flags@NixStyleFlags {..} _extraArgs globalFlags = do then do conf <- runRebuild (distProjectRootDirectory . distDirLayout $ baseCtx) $ readProjectLocalExtraConfig v (distDirLayout baseCtx) - return (baseCtx, conf <> cliConfig) + return (baseCtx, (fst $ ignoreConditions conf) <> cliConfig) -- TODO ugh. maybe don't have extra configs have conditions, only main configs else return (baseCtx, cliConfig) where diff --git a/cabal-install/src/Distribution/Client/CmdOutdated.hs b/cabal-install/src/Distribution/Client/CmdOutdated.hs index b2b817a00ce..f386ff26791 100644 --- a/cabal-install/src/Distribution/Client/CmdOutdated.hs +++ b/cabal-install/src/Distribution/Client/CmdOutdated.hs @@ -302,7 +302,7 @@ depsFromNewFreezeFile verbosity mprojectFile = do projectConfig <- runRebuild (distProjectRootDirectory distDirLayout) $ readProjectLocalFreezeConfig verbosity distDirLayout let ucnstrs = map fst . projectConfigConstraints . projectConfigShared - $ projectConfig + $ undefined projectConfig -- TODO instantiate deps = userConstraintsToDependencies ucnstrs freezeFile = distProjectFile distDirLayout "freeze" freezeFileExists <- doesFileExist freezeFile diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index 17c0435035d..a80205e9c57 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -503,13 +503,13 @@ withProjectOrGlobalConfig' verbosity globalConfigFlag with without = do readProjectConfig :: Verbosity -> Flag FilePath -> DistDirLayout - -> Rebuild ProjectConfig + -> Rebuild ProjectConfigSkeleton readProjectConfig verbosity configFileFlag distDirLayout = do global <- readGlobalConfig verbosity configFileFlag local <- readProjectLocalConfigOrDefault verbosity distDirLayout freeze <- readProjectLocalFreezeConfig verbosity distDirLayout extra <- readProjectLocalExtraConfig verbosity distDirLayout - return (global <> local <> freeze <> extra) + return (singletonProjectConfigSkeleton global <> local <> freeze <> extra) -- | Reads an explicit @cabal.project@ file in the given project root dir, @@ -517,7 +517,7 @@ readProjectConfig verbosity configFileFlag distDirLayout = do -- readProjectLocalConfigOrDefault :: Verbosity -> DistDirLayout - -> Rebuild ProjectConfig + -> Rebuild ProjectConfigSkeleton readProjectLocalConfigOrDefault verbosity distDirLayout = do usesExplicitProjectRoot <- liftIO $ doesFileExist projectFile if usesExplicitProjectRoot @@ -525,7 +525,7 @@ readProjectLocalConfigOrDefault verbosity distDirLayout = do readProjectFile verbosity distDirLayout "" "project file" else do monitorFiles [monitorNonExistentFile projectFile] - return defaultImplicitProjectConfig + return (singletonProjectConfigSkeleton defaultImplicitProjectConfig) where projectFile = distProjectFile distDirLayout "" @@ -544,7 +544,7 @@ readProjectLocalConfigOrDefault verbosity distDirLayout = do -- principle can be edited manually or by other tools. -- readProjectLocalExtraConfig :: Verbosity -> DistDirLayout - -> Rebuild ProjectConfig + -> Rebuild ProjectConfigSkeleton readProjectLocalExtraConfig verbosity distDirLayout = readProjectFile verbosity distDirLayout "local" "project local configuration file" @@ -554,7 +554,7 @@ readProjectLocalExtraConfig verbosity distDirLayout = -- principle can be edited manually or by other tools. -- readProjectLocalFreezeConfig :: Verbosity -> DistDirLayout - -> Rebuild ProjectConfig + -> Rebuild ProjectConfigSkeleton readProjectLocalFreezeConfig verbosity distDirLayout = readProjectFile verbosity distDirLayout "freeze" "project freeze file" @@ -565,13 +565,14 @@ readProjectFile :: Verbosity -> DistDirLayout -> String -> String - -> Rebuild ProjectConfig + -> Rebuild ProjectConfigSkeleton readProjectFile verbosity DistDirLayout{distProjectFile} extensionName extensionDescription = do exists <- liftIO $ doesFileExist extensionFile if exists then do monitorFiles [monitorFileHashed extensionFile] - addProjectFileProvenance <$> liftIO readExtensionFile + -- addProjectFileProvenance <$> + liftIO readExtensionFile else do monitorFiles [monitorNonExistentFile extensionFile] return mempty where @@ -579,26 +580,24 @@ readProjectFile verbosity DistDirLayout{distProjectFile} readExtensionFile = reportParseResult verbosity extensionDescription extensionFile - . (parseProjectConfig extensionFile) + =<< parseProjectSkeleton extensionFile =<< BS.readFile extensionFile - +{- addProjectFileProvenance config = config { - projectConfigProvenance = - Set.insert (Explicit extensionFile) (projectConfigProvenance config) + projectConfigProvenance = _ config + -- Set.insert (Explicit extensionFile) (projectConfigProvenance config) } - - +-} +{- -- | Parse the 'ProjectConfig' format. -- -- For the moment this is implemented in terms of parsers for legacy -- configuration types, plus a conversion. -- -parseProjectConfig :: FilePath -> BS.ByteString -> OldParser.ParseResult ProjectConfig -parseProjectConfig source content = - convertLegacyProjectConfig <$> - (parseLegacyProjectConfig source content) - +parseProjectConfigSkeleton :: FilePath -> BS.ByteString -> IO (OldParser.ParseResult ProjectConfigSkeleton) +parseProjectConfigSkeleton source content = (parseProjectSkeleton source content) +-} -- | Render the 'ProjectConfig' format. -- diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 84d6e4455c2..77179f499b9 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -1,9 +1,15 @@ -{-# LANGUAGE RecordWildCards, NamedFieldPuns, DeriveGeneric, ConstraintKinds #-} +{-# LANGUAGE RecordWildCards, NamedFieldPuns, DeriveGeneric, ConstraintKinds, FlexibleInstances #-} -- | Project configuration, implementation in terms of legacy types. -- module Distribution.Client.ProjectConfig.Legacy ( + -- Project config skeletons + ProjectConfigSkeleton, + parseProjectSkeleton, + instantiateProjectConfigSkeleton, + singletonProjectConfigSkeleton, + -- * Project config in terms of legacy types LegacyProjectConfig, parseLegacyProjectConfig, @@ -17,13 +23,12 @@ module Distribution.Client.ProjectConfig.Legacy ( -- * Internals, just for tests parsePackageLocationTokenQ, - renderPackageLocationToken, + renderPackageLocationToken ) where -import Prelude () import Distribution.Client.Compat.Prelude -import Distribution.Types.Flag (parsecFlagAssignment) +import Distribution.Types.Flag (parsecFlagAssignment, FlagName) import Distribution.Client.ProjectConfig.Types import Distribution.Client.Types.RepoName (RepoName (..), unRepoName) @@ -43,9 +48,10 @@ import Distribution.Solver.Types.ConstraintSource import Distribution.FieldGrammar import Distribution.Package import Distribution.Types.SourceRepo (RepoType) -import Distribution.Types.CondTree (CondTree (..), CondBranch (..), condIfThen, condIfThenElse) +import Distribution.Types.CondTree (CondTree (..), CondBranch (..), condIfThen, condIfThenElse, mapTreeConds) import Distribution.PackageDescription ( dispFlagAssignment, Condition (..), ConfVar (..), FlagAssignment (..) ) +import Distribution.PackageDescription.Configuration (simplifyWithSysParams) import Distribution.Simple.Compiler ( OptimisationLevel(..), DebugInfoLevel(..), CompilerInfo(..) ) import Distribution.Simple.InstallDirs ( CopyDest (NoCopyDest) ) @@ -94,9 +100,10 @@ import Distribution.Simple.Command import Distribution.Types.PackageVersionConstraint ( PackageVersionConstraint ) import Distribution.Parsec (ParsecParser, zeroPos) -import Distribution.System (Platform (..)) +import Distribution.System (Platform (..), OS, Arch) import qualified Data.Map as Map +import qualified Data.Set as Set import qualified Data.ByteString.Char8 as BS import Network.URI (URI (..)) @@ -107,21 +114,119 @@ import Distribution.Fields.Field (SectionArg (..)) ------------------------------------------------------------------ --- Representing the project config file in terms of legacy types +-- Handle extended project config files with conditionals and imports. -- --- ProjectConfigSkeleton is a tree of conditional blocks and imports wrapping a legacy config. It can be finalized by providing the conditional resolution invo +-- | ProjectConfigSkeleton is a tree of conditional blocks and imports wrapping a config. It can be finalized by providing the conditional resolution info -- and then resolving and downloading the imports - +type ProjectConfigSkeleton = CondTree ConfVar [ProjectConfigImport] ProjectConfig type ProjectConfigImport = String -type ProjectConfigSkeleton = CondTree ConfVar [ProjectConfigImport] LegacyProjectConfig -finalizeProjectConfigSkeleton :: Platform -> CompilerInfo -> FlagAssignment -> ProjectConfigSkeleton -> IO LegacyProjectConfig -finalizeProjectConfigSkeleton = undefined +instance Semigroup (CondTree ConfVar [ProjectConfigImport] ProjectConfig) where + (CondNode a c bs) <> (CondNode a' c' bs') = CondNode (a <> a') (c <> c') (bs <> bs') + +instance Monoid (CondTree ConfVar [ProjectConfigImport] ProjectConfig) where + mappend = (<>) + mempty = CondNode mempty mempty mempty + +singletonProjectConfigSkeleton :: ProjectConfig -> ProjectConfigSkeleton +singletonProjectConfigSkeleton x = CondNode x mempty mempty + +instantiateProjectConfigSkeleton :: OS -> Arch -> CompilerInfo -> FlagAssignment -> ProjectConfigSkeleton -> ProjectConfig +instantiateProjectConfigSkeleton os arch impl flags skel = go $ mapTreeConds (fst . simplifyWithSysParams os arch impl) skel -- TODO bail on flags + where + go :: CondTree + FlagName + [ProjectConfigImport] + ProjectConfig + -> ProjectConfig + go (CondNode l _imps ts) = + let branches = concatMap processBranch ts + in l <> mconcat branches + processBranch (CondBranch cnd t mf) = case cnd of + (Lit True) -> [go t] + (Lit False) -> maybe ([]) ((:[]) . go) mf + _ -> [] + +{- + + where + go :: CondTree + FlagName + [ProjectConfigImport] + ProjectConfig + -> IO (ParseResult ProjectConfig) + go (CondNode l imps ts) = do + -- ugly code to avoid defining legacy parse results explicitly as a monad transformer. shrug. + impSkel <- sequenceA <$> mapM readImportConfig imps + impConfig <- fmap (fmap mconcat) . fmap (join . fmap sequenceA) . sequenceA $ fmap (mapM (instantiateProjectConfigSkeleton os arch impl flags)) impSkel + branches <- mconcatParse . concat <$> mapM processBranch ts + pure $ mconcatParse [pure l, impConfig, branches] + processBranch (CondBranch cnd t mf) = case cnd of + (Lit True) -> (:[]) <$> go t + (Lit False) -> maybe (pure []) (fmap (:[]) . go) mf + _ -> pure [] + mconcatParse = fmap mconcat . sequenceA +-} + +-- TODO ensure parse doesn't have flags setting compiler inside conditionals +parseProjectSkeleton :: FilePath -> BS.ByteString -> IO (ParseResult ProjectConfigSkeleton) +parseProjectSkeleton source bs = runInnerParsers <$> linesToNode (BS.lines bs) + where + linesToNode :: [BS.ByteString] -> IO (CondTree ConfVar [ProjectConfigImport] [BS.ByteString]) + linesToNode ls = packResult . mconcat <$> go ls + + packResult :: ([CondBranch ConfVar [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString]) -> CondTree ConfVar [ProjectConfigImport] [BS.ByteString] + packResult (branches, imps, ls) = CondNode ls imps branches + + go :: [BS.ByteString] -> IO [([CondBranch ConfVar [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString])] + go (l:ls) + | Just condition <- parseCond l = + let (clause, rest) = splitTillIndented ls + in case rest of + (r:rs) | (BS.pack "else") `BS.isPrefixOf` r -> -- TODO handle elif + let (elseClause, lastRest) = splitTillIndented rs + in do + c1 <- linesToNode clause + c2 <- linesToNode elseClause + (([condIfThenElse condition c1 c2], [], []) :) <$> go lastRest + _ -> do + c1 <- linesToNode clause + (([condIfThen condition c1], [], []) :) <$> go rest + | Just imp <- parseImport l = do x <- go . BS.lines =<< fetchImportConfig imp + ((([], [imp], []) : x) ++) <$> go ls + | otherwise = (([], [], [l]) :) <$> go ls + go [] = pure [] + + splitTillIndented = span ((BS.pack " ") `BS.isPrefixOf`) + + parseCond :: BS.ByteString -> Maybe (Condition ConfVar) + parseCond l | (BS.pack "if(") `BS.isPrefixOf` l = case FPR.runParseResult (parseConditionConfVar [SecArgOther zeroPos (BS.takeWhile (/=')') $ BS.drop 3 l)]) of + (_, Left _) -> Nothing + (_, Right x) -> Just x + + | otherwise = Nothing + parseImport l | (BS.pack "import ") `BS.isPrefixOf` l = Just . BS.unpack $ BS.drop (length "import ") l + | otherwise = Nothing + runInnerParsers :: CondTree ConfVar [ProjectConfigImport] [BS.ByteString] -> ParseResult ProjectConfigSkeleton + runInnerParsers = traverse (fmap (addProvenance . convertLegacyProjectConfig) . parseLegacyProjectConfig source . BS.unlines) + + addProvenance x = x {projectConfigProvenance = Set.singleton (Explicit source)} + fetchImportConfig :: ProjectConfigImport -> IO BS.ByteString + fetchImportConfig = BS.readFile -- todo, handle http +{- +-- todo handlehttp +readImportConfig :: ProjectConfigImport -> IO (ParseResult ProjectConfigSkeleton) +readImportConfig x = parseProjectSkeleton x <$> BS.readFile x +-- todo add extra files to file change monitor +-} +------------------------------------------------------------------ +-- Representing the project config file in terms of legacy types +-- -- | We already have parsers\/pretty-printers for almost all the fields in the -- project config file, but they're in terms of the types used for the command @@ -875,33 +980,6 @@ convertToLegacyPerPackageConfig PackageConfig {..} = -- -parseLegacyProjectSkeleton :: FilePath -> BS.ByteString -> ParseResult ProjectConfigSkeleton -parseLegacyProjectSkeleton source bs = _ . packResult . mconcat . go $ BS.lines bs - where - go :: [BS.ByteString] -> [([CondBranch ConfVar [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString])] - go (l:ls) - | Just condition <- parseCond l = - let (clause, rest) = splitTillIndented ls - in case rest of - (r:rs) | (BS.pack "else") `BS.isPrefixOf` r -> -- TODO handle elif - let (elseClause, lastRest) = splitTillIndented rs - in ([condIfThenElse condition (clauseToNode clause) (clauseToNode elseClause)], [], []) : go lastRest - _ -> ([condIfThen condition (clauseToNode clause)], [], []) : go rest - | Just imp <- parseImport l = ([], [imp], []) : go ls - | otherwise = ([], [], [l]) : go ls - packResult :: ([CondBranch ConfVar [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString]) -> CondTree ConfVar [ProjectConfigImport] [BS.ByteString] - packResult (branches, imps, ls) = CondNode ls imps branches - splitTillIndented = span ((BS.pack " ") `BS.isPrefixOf`) - clauseToNode ls = CondNode ls [] [] -- TODO extract imports from lines - parseCond :: BS.ByteString -> Maybe (Condition ConfVar) - parseCond l | (BS.pack "if(") `BS.isPrefixOf` l = case FPR.runParseResult (parseConditionConfVar [SecArgOther zeroPos (BS.drop 3 l)]) of -- todo drop end also - (_, Left _) -> Nothing - (_, Right x) -> Just x - - | otherwise = Nothing - parseImport l | (BS.pack "import ") `BS.isPrefixOf` l = Just . BS.unpack $ BS.drop (length "import ") l - | otherwise = Nothing - parseLegacyProjectConfig :: FilePath -> BS.ByteString -> ParseResult LegacyProjectConfig parseLegacyProjectConfig source = parseConfig (legacyProjectConfigFieldDescrs constraintSrc) diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index b166c03cbb8..d37d9a4563d 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -76,6 +76,7 @@ import Distribution.Client.PackageHash import Distribution.Client.RebuildMonad import Distribution.Client.Store import Distribution.Client.ProjectConfig +import Distribution.Client.ProjectConfig.Legacy import Distribution.Client.ProjectPlanOutput import Distribution.Client.Types @@ -322,10 +323,12 @@ rebuildProjectConfig verbosity runRebuild distProjectRootDirectory $ rerunIfChanged verbosity fileMonitorProjectConfig - fileMonitorProjectConfigKey + fileMonitorProjectConfigKey -- todo check deps too? $ do liftIO $ info verbosity "Project settings changed, reconfiguring..." - projectConfig <- phaseReadProjectConfig + projectConfigSkeleton <- phaseReadProjectConfig -- ignoreConditions + (compiler, Platform arch os, _) <- configureCompiler verbosity distDirLayout (fst $ PD.ignoreConditions projectConfigSkeleton) + let projectConfig = instantiateProjectConfigSkeleton os arch (compilerInfo compiler) mempty projectConfigSkeleton localPackages <- phaseReadLocalPackages projectConfig return (projectConfig, localPackages) @@ -353,7 +356,7 @@ rebuildProjectConfig verbosity -- Read the cabal.project (or implicit config) and combine it with -- arguments from the command line -- - phaseReadProjectConfig :: Rebuild ProjectConfig + phaseReadProjectConfig :: Rebuild ProjectConfigSkeleton phaseReadProjectConfig = do readProjectConfig verbosity projectConfigConfigFile distDirLayout @@ -380,6 +383,63 @@ rebuildProjectConfig verbosity pkgLocations +configureCompiler :: Verbosity -> + DistDirLayout -> + ProjectConfig -> + Rebuild (Compiler, Platform, ProgramDb) +configureCompiler verbosity + DistDirLayout { + distProjectCacheFile + } + ProjectConfig { + projectConfigShared = ProjectConfigShared { + projectConfigHcFlavor, + projectConfigHcPath, + projectConfigHcPkg + }, + projectConfigLocalPackages = PackageConfig { + packageConfigProgramPaths, + packageConfigProgramArgs, + packageConfigProgramPathExtra + } + } = do + let fileMonitorCompiler = newFileMonitor . distProjectCacheFile $ "compiler" + + progsearchpath <- liftIO $ getSystemSearchPath + rerunIfChanged verbosity fileMonitorCompiler + (hcFlavor, hcPath, hcPkg, progsearchpath, + packageConfigProgramPaths, + packageConfigProgramArgs, + packageConfigProgramPathExtra) $ do + + liftIO $ info verbosity "Compiler settings changed, reconfiguring..." + result@(_, _, progdb') <- liftIO $ + Cabal.configCompilerEx + hcFlavor hcPath hcPkg + progdb verbosity + + -- Note that we added the user-supplied program locations and args + -- for /all/ programs, not just those for the compiler prog and + -- compiler-related utils. In principle we don't know which programs + -- the compiler will configure (and it does vary between compilers). + -- We do know however that the compiler will only configure the + -- programs it cares about, and those are the ones we monitor here. + monitorFiles (programsMonitorFiles progdb') + + return result + where + hcFlavor = flagToMaybe projectConfigHcFlavor + hcPath = flagToMaybe projectConfigHcPath + hcPkg = flagToMaybe projectConfigHcPkg + progdb = + userSpecifyPaths (Map.toList (getMapLast packageConfigProgramPaths)) + . userSpecifyArgss (Map.toList (getMapMappend packageConfigProgramArgs)) + . modifyProgramSearchPath + (++ [ ProgramSearchPathDir dir + | dir <- fromNubList packageConfigProgramPathExtra ]) + $ defaultProgramDb + + -- | Return an up-to-date elaborated install plan. -- -- Two variants of the install plan are returned: with and without packages @@ -451,7 +511,6 @@ rebuildInstallPlan verbosity return (improvedPlan, elaboratedPlan, elaboratedShared, totalIndexState, activeRepos) where - fileMonitorCompiler = newFileMonitorInCacheDir "compiler" fileMonitorSolverPlan = newFileMonitorInCacheDir "solver-plan" fileMonitorSourceHashes = newFileMonitorInCacheDir "source-hashes" fileMonitorElaboratedPlan = newFileMonitorInCacheDir "elaborated-plan" @@ -468,7 +527,9 @@ rebuildInstallPlan verbosity -- phaseConfigureCompiler :: ProjectConfig -> Rebuild (Compiler, Platform, ProgramDb) - phaseConfigureCompiler ProjectConfig { + phaseConfigureCompiler = configureCompiler verbosity distDirLayout +{- + ProjectConfig { projectConfigShared = ProjectConfigShared { projectConfigHcFlavor, projectConfigHcPath, @@ -513,7 +574,7 @@ rebuildInstallPlan verbosity (++ [ ProgramSearchPathDir dir | dir <- fromNubList packageConfigProgramPathExtra ]) $ defaultProgramDb - +-} -- Configuring other programs. -- diff --git a/cabal-install/src/Distribution/Deprecated/ParseUtils.hs b/cabal-install/src/Distribution/Deprecated/ParseUtils.hs index 78f57b58d78..c1a0e4cd30c 100644 --- a/cabal-install/src/Distribution/Deprecated/ParseUtils.hs +++ b/cabal-install/src/Distribution/Deprecated/ParseUtils.hs @@ -106,6 +106,14 @@ instance Monad ParseResult where ParseFailed err -> ParseFailed err ParseOk ws' x' -> ParseOk (ws'++ws) x' +instance Foldable ParseResult where + foldMap _ (ParseFailed _ ) = mempty + foldMap f (ParseOk _ x) = f x + +instance Traversable ParseResult where + traverse _ (ParseFailed err) = pure (ParseFailed err) + traverse f (ParseOk ws x) = ParseOk ws <$> f x + #if !(MIN_VERSION_base(4,9,0)) fail = parseResultFail #elif !(MIN_VERSION_base(4,13,0)) From e807d2db0079e9b111ee583989a2313e4c471f37 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Mon, 29 Nov 2021 18:29:35 -0500 Subject: [PATCH 03/22] get more stuff sort of working --- .../src/Distribution/Client/CmdConfigure.hs | 1 - .../src/Distribution/Client/CmdOutdated.hs | 2 +- .../src/Distribution/Client/ProjectConfig.hs | 60 ++++++++++++------- .../Client/ProjectConfig/Legacy.hs | 21 ------- .../Distribution/Client/ProjectPlanning.hs | 4 +- 5 files changed, 41 insertions(+), 47 deletions(-) diff --git a/cabal-install/src/Distribution/Client/CmdConfigure.hs b/cabal-install/src/Distribution/Client/CmdConfigure.hs index 372b8eb1a32..dd30de8af2b 100644 --- a/cabal-install/src/Distribution/Client/CmdConfigure.hs +++ b/cabal-install/src/Distribution/Client/CmdConfigure.hs @@ -36,7 +36,6 @@ import Distribution.Client.DistDirLayout ( DistDirLayout(..) ) import Distribution.Client.RebuildMonad (runRebuild) import Distribution.Client.ProjectConfig.Types -import Distribution.Types.CondTree (ignoreConditions) configureCommand :: CommandUI (NixStyleFlags ()) configureCommand = CommandUI { diff --git a/cabal-install/src/Distribution/Client/CmdOutdated.hs b/cabal-install/src/Distribution/Client/CmdOutdated.hs index f386ff26791..b2b817a00ce 100644 --- a/cabal-install/src/Distribution/Client/CmdOutdated.hs +++ b/cabal-install/src/Distribution/Client/CmdOutdated.hs @@ -302,7 +302,7 @@ depsFromNewFreezeFile verbosity mprojectFile = do projectConfig <- runRebuild (distProjectRootDirectory distDirLayout) $ readProjectLocalFreezeConfig verbosity distDirLayout let ucnstrs = map fst . projectConfigConstraints . projectConfigShared - $ undefined projectConfig -- TODO instantiate + $ projectConfig deps = userConstraintsToDependencies ucnstrs freezeFile = distProjectFile distDirLayout "freeze" freezeFileExists <- doesFileExist freezeFile diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index a80205e9c57..2c21a11708e 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -505,11 +505,11 @@ readProjectConfig :: Verbosity -> DistDirLayout -> Rebuild ProjectConfigSkeleton readProjectConfig verbosity configFileFlag distDirLayout = do - global <- readGlobalConfig verbosity configFileFlag + global <- singletonProjectConfigSkeleton <$> readGlobalConfig verbosity configFileFlag local <- readProjectLocalConfigOrDefault verbosity distDirLayout - freeze <- readProjectLocalFreezeConfig verbosity distDirLayout - extra <- readProjectLocalExtraConfig verbosity distDirLayout - return (singletonProjectConfigSkeleton global <> local <> freeze <> extra) + freeze <- singletonProjectConfigSkeleton <$> readProjectLocalFreezeConfig verbosity distDirLayout + extra <- singletonProjectConfigSkeleton <$> readProjectLocalExtraConfig verbosity distDirLayout + return (global <> local <> freeze <> extra) -- | Reads an explicit @cabal.project@ file in the given project root dir, @@ -522,7 +522,7 @@ readProjectLocalConfigOrDefault verbosity distDirLayout = do usesExplicitProjectRoot <- liftIO $ doesFileExist projectFile if usesExplicitProjectRoot then do - readProjectFile verbosity distDirLayout "" "project file" + readProjectFileSkeleton verbosity distDirLayout "" "project file" else do monitorFiles [monitorNonExistentFile projectFile] return (singletonProjectConfigSkeleton defaultImplicitProjectConfig) @@ -544,7 +544,7 @@ readProjectLocalConfigOrDefault verbosity distDirLayout = do -- principle can be edited manually or by other tools. -- readProjectLocalExtraConfig :: Verbosity -> DistDirLayout - -> Rebuild ProjectConfigSkeleton + -> Rebuild ProjectConfig readProjectLocalExtraConfig verbosity distDirLayout = readProjectFile verbosity distDirLayout "local" "project local configuration file" @@ -554,24 +554,19 @@ readProjectLocalExtraConfig verbosity distDirLayout = -- principle can be edited manually or by other tools. -- readProjectLocalFreezeConfig :: Verbosity -> DistDirLayout - -> Rebuild ProjectConfigSkeleton + -> Rebuild ProjectConfig readProjectLocalFreezeConfig verbosity distDirLayout = readProjectFile verbosity distDirLayout "freeze" "project freeze file" --- | Reads a named config file in the given project root dir, or returns empty. +-- | Reads a named extended (with imports and conditionals) config file in the given project root dir, or returns empty. -- -readProjectFile :: Verbosity - -> DistDirLayout - -> String - -> String - -> Rebuild ProjectConfigSkeleton -readProjectFile verbosity DistDirLayout{distProjectFile} +readProjectFileSkeleton :: Verbosity -> DistDirLayout -> String -> String -> Rebuild ProjectConfigSkeleton +readProjectFileSkeleton verbosity DistDirLayout{distProjectFile} extensionName extensionDescription = do exists <- liftIO $ doesFileExist extensionFile if exists then do monitorFiles [monitorFileHashed extensionFile] - -- addProjectFileProvenance <$> liftIO readExtensionFile else do monitorFiles [monitorNonExistentFile extensionFile] return mempty @@ -582,22 +577,41 @@ readProjectFile verbosity DistDirLayout{distProjectFile} reportParseResult verbosity extensionDescription extensionFile =<< parseProjectSkeleton extensionFile =<< BS.readFile extensionFile -{- + +-- | Reads a named config file in the given project root dir, or returns empty. +-- +readProjectFile :: Verbosity -> DistDirLayout -> String -> String -> Rebuild ProjectConfig +readProjectFile verbosity DistDirLayout{distProjectFile} + extensionName extensionDescription = do + exists <- liftIO $ doesFileExist extensionFile + if exists + then do monitorFiles [monitorFileHashed extensionFile] + addProjectFileProvenance <$> liftIO readExtensionFile + else do monitorFiles [monitorNonExistentFile extensionFile] + return mempty + where + extensionFile = distProjectFile extensionName + + readExtensionFile = + reportParseResult verbosity extensionDescription extensionFile + . (parseProjectConfig extensionFile) + =<< BS.readFile extensionFile + addProjectFileProvenance config = config { - projectConfigProvenance = _ config - -- Set.insert (Explicit extensionFile) (projectConfigProvenance config) + projectConfigProvenance = Set.insert (Explicit extensionFile) (projectConfigProvenance config) } --} -{- + + -- | Parse the 'ProjectConfig' format. -- -- For the moment this is implemented in terms of parsers for legacy -- configuration types, plus a conversion. -- -parseProjectConfigSkeleton :: FilePath -> BS.ByteString -> IO (OldParser.ParseResult ProjectConfigSkeleton) -parseProjectConfigSkeleton source content = (parseProjectSkeleton source content) --} +parseProjectConfig :: FilePath -> BS.ByteString -> OldParser.ParseResult ProjectConfig +parseProjectConfig source content = + convertLegacyProjectConfig <$> + (parseLegacyProjectConfig source content) -- | Render the 'ProjectConfig' format. -- diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 77179f499b9..747fe409abd 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -149,27 +149,6 @@ instantiateProjectConfigSkeleton os arch impl flags skel = go $ mapTreeConds (fs (Lit False) -> maybe ([]) ((:[]) . go) mf _ -> [] -{- - - where - go :: CondTree - FlagName - [ProjectConfigImport] - ProjectConfig - -> IO (ParseResult ProjectConfig) - go (CondNode l imps ts) = do - -- ugly code to avoid defining legacy parse results explicitly as a monad transformer. shrug. - impSkel <- sequenceA <$> mapM readImportConfig imps - impConfig <- fmap (fmap mconcat) . fmap (join . fmap sequenceA) . sequenceA $ fmap (mapM (instantiateProjectConfigSkeleton os arch impl flags)) impSkel - branches <- mconcatParse . concat <$> mapM processBranch ts - pure $ mconcatParse [pure l, impConfig, branches] - processBranch (CondBranch cnd t mf) = case cnd of - (Lit True) -> (:[]) <$> go t - (Lit False) -> maybe (pure []) (fmap (:[]) . go) mf - _ -> pure [] - mconcatParse = fmap mconcat . sequenceA --} - -- TODO ensure parse doesn't have flags setting compiler inside conditionals parseProjectSkeleton :: FilePath -> BS.ByteString -> IO (ParseResult ProjectConfigSkeleton) parseProjectSkeleton source bs = runInnerParsers <$> linesToNode (BS.lines bs) diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index d37d9a4563d..b555605fad9 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -326,7 +326,9 @@ rebuildProjectConfig verbosity fileMonitorProjectConfigKey -- todo check deps too? $ do liftIO $ info verbosity "Project settings changed, reconfiguring..." - projectConfigSkeleton <- phaseReadProjectConfig -- ignoreConditions + liftIO $ createDirectoryIfMissingVerbose verbosity True distProjectCacheDirectory + projectConfigSkeleton <- phaseReadProjectConfig + -- have to create the cache directory before configuring the compiler (compiler, Platform arch os, _) <- configureCompiler verbosity distDirLayout (fst $ PD.ignoreConditions projectConfigSkeleton) let projectConfig = instantiateProjectConfigSkeleton os arch (compilerInfo compiler) mempty projectConfigSkeleton localPackages <- phaseReadLocalPackages projectConfig From ca7499a3b7af597c550108d6fb8b0067d97b461f Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Mon, 29 Nov 2021 20:55:33 -0500 Subject: [PATCH 04/22] conditional parsing actually works --- Cabal/src/Distribution/Fields/ConfVar.hs | 11 +++++++++-- .../src/Distribution/Client/CmdConfigure.hs | 2 +- .../Client/ProjectConfig/Legacy.hs | 19 +++++++++++++------ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Cabal/src/Distribution/Fields/ConfVar.hs b/Cabal/src/Distribution/Fields/ConfVar.hs index 68601cd53e9..533dfd5e824 100644 --- a/Cabal/src/Distribution/Fields/ConfVar.hs +++ b/Cabal/src/Distribution/Fields/ConfVar.hs @@ -1,14 +1,15 @@ {-# LANGUAGE OverloadedStrings #-} -module Distribution.Fields.ConfVar (parseConditionConfVar) where +module Distribution.Fields.ConfVar (parseConditionConfVar, parseConditionConfVarFromClause) where import Distribution.Compat.CharParsing (char, integral) import Distribution.Compat.Prelude -import Distribution.Fields.Field (SectionArg (..)) +import Distribution.Fields.Field (SectionArg (..), Field(..)) import Distribution.Fields.ParseResult import Distribution.Parsec (Parsec (..), Position (..), runParsecParser) import Distribution.Parsec.FieldLineStream (fieldLineStreamFromBS) import Distribution.Types.Condition import Distribution.Types.ConfVar (ConfVar (..)) +import Distribution.Fields.Parser (readFields) import Distribution.Version (anyVersion, earlierVersion, intersectVersionRanges, laterVersion, majorBoundVersion, mkVersion, noVersion, orEarlierVersion, orLaterVersion, thisVersion, unionVersionRanges, @@ -17,6 +18,12 @@ import Prelude () import qualified Text.Parsec as P import qualified Text.Parsec.Error as P +import qualified Data.ByteString.Char8 as B8 + +parseConditionConfVarFromClause :: B8.ByteString -> Either P.ParseError (Condition ConfVar) +parseConditionConfVarFromClause x = readFields x >>= \r -> case r of + (Section _ xs _ : _ ) -> P.runParser (parser <* P.eof) () "" xs + _ -> error "guh" -- | Parse @'Condition' 'ConfVar'@ from section arguments provided by parsec -- based outline parser. diff --git a/cabal-install/src/Distribution/Client/CmdConfigure.hs b/cabal-install/src/Distribution/Client/CmdConfigure.hs index dd30de8af2b..f734f15c668 100644 --- a/cabal-install/src/Distribution/Client/CmdConfigure.hs +++ b/cabal-install/src/Distribution/Client/CmdConfigure.hs @@ -128,7 +128,7 @@ configureAction' flags@NixStyleFlags {..} _extraArgs globalFlags = do then do conf <- runRebuild (distProjectRootDirectory . distDirLayout $ baseCtx) $ readProjectLocalExtraConfig v (distDirLayout baseCtx) - return (baseCtx, (fst $ ignoreConditions conf) <> cliConfig) -- TODO ugh. maybe don't have extra configs have conditions, only main configs + return (baseCtx, conf <> cliConfig) -- TODO ugh. maybe don't have extra configs have conditions, only main configs else return (baseCtx, cliConfig) where diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 747fe409abd..5379d877c43 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -108,11 +108,13 @@ import qualified Data.ByteString.Char8 as BS import Network.URI (URI (..)) -import Distribution.Fields.ConfVar (parseConditionConfVar) +import Distribution.Fields.ConfVar (parseConditionConfVarFromClause) import qualified Distribution.Fields.ParseResult as FPR import Distribution.Fields.Field (SectionArg (..)) + + ------------------------------------------------------------------ -- Handle extended project config files with conditionals and imports. -- @@ -149,7 +151,10 @@ instantiateProjectConfigSkeleton os arch impl flags skel = go $ mapTreeConds (fs (Lit False) -> maybe ([]) ((:[]) . go) mf _ -> [] + -- TODO ensure parse doesn't have flags setting compiler inside conditionals + +-- NOTE a nice refactor would be to use readFields directly to get a tree structure. parseProjectSkeleton :: FilePath -> BS.ByteString -> IO (ParseResult ProjectConfigSkeleton) parseProjectSkeleton source bs = runInnerParsers <$> linesToNode (BS.lines bs) where @@ -181,9 +186,9 @@ parseProjectSkeleton source bs = runInnerParsers <$> linesToNode (BS.lines bs) splitTillIndented = span ((BS.pack " ") `BS.isPrefixOf`) parseCond :: BS.ByteString -> Maybe (Condition ConfVar) - parseCond l | (BS.pack "if(") `BS.isPrefixOf` l = case FPR.runParseResult (parseConditionConfVar [SecArgOther zeroPos (BS.takeWhile (/=')') $ BS.drop 3 l)]) of - (_, Left _) -> Nothing - (_, Right x) -> Just x + parseCond l | (BS.pack "if(") `BS.isPrefixOf` l = case parseConditionConfVarFromClause l of + Left err -> error (show err) -- TODO improve error reporting here + Right x -> Just x | otherwise = Nothing parseImport l | (BS.pack "import ") `BS.isPrefixOf` l = Just . BS.unpack $ BS.drop (length "import ") l @@ -198,9 +203,11 @@ parseProjectSkeleton source bs = runInnerParsers <$> linesToNode (BS.lines bs) {- -- todo handlehttp -readImportConfig :: ProjectConfigImport -> IO (ParseResult ProjectConfigSkeleton) -readImportConfig x = parseProjectSkeleton x <$> BS.readFile x -- todo add extra files to file change monitor + +-- TODO handle importing legacy freeze as well +-- TODO handle merge semantics for constraints specially + -} ------------------------------------------------------------------ From 0c6e6e326c9924bc9ef254a3820bf4a2aeb31fd6 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Mon, 3 Jan 2022 15:08:04 -0500 Subject: [PATCH 05/22] error cleanup and downloads --- Cabal/src/Distribution/Fields/ConfVar.hs | 3 +- Cabal/src/Distribution/Types/CondTree.hs | 9 +- .../Client/ProjectConfig/Legacy.hs | 93 ++++++++++++------- 3 files changed, 68 insertions(+), 37 deletions(-) diff --git a/Cabal/src/Distribution/Fields/ConfVar.hs b/Cabal/src/Distribution/Fields/ConfVar.hs index 533dfd5e824..b045c3ef172 100644 --- a/Cabal/src/Distribution/Fields/ConfVar.hs +++ b/Cabal/src/Distribution/Fields/ConfVar.hs @@ -17,13 +17,14 @@ import Distribution.Version import Prelude () import qualified Text.Parsec as P +import qualified Text.Parsec.Pos as P import qualified Text.Parsec.Error as P import qualified Data.ByteString.Char8 as B8 parseConditionConfVarFromClause :: B8.ByteString -> Either P.ParseError (Condition ConfVar) parseConditionConfVarFromClause x = readFields x >>= \r -> case r of (Section _ xs _ : _ ) -> P.runParser (parser <* P.eof) () "" xs - _ -> error "guh" + _ -> Left $ P.newErrorMessage (P.Message "No fields in clause") (P.initialPos "") -- | Parse @'Condition' 'ConfVar'@ from section arguments provided by parsec -- based outline parser. diff --git a/Cabal/src/Distribution/Types/CondTree.hs b/Cabal/src/Distribution/Types/CondTree.hs index 8fd233658f5..442e4b48b8c 100644 --- a/Cabal/src/Distribution/Types/CondTree.hs +++ b/Cabal/src/Distribution/Types/CondTree.hs @@ -68,6 +68,13 @@ instance (Binary v, Binary c, Binary a) => Binary (CondTree v c a) instance (Structured v, Structured c, Structured a) => Structured (CondTree v c a) instance (NFData v, NFData c, NFData a) => NFData (CondTree v c a) where rnf = genericRnf +instance (Semigroup a, Semigroup c) => Semigroup (CondTree v c a) where + (CondNode a c bs) <> (CondNode a' c' bs') = CondNode (a <> a') (c <> c') (bs <> bs') + +instance (Monoid a, Monoid c) => Monoid (CondTree v c a) where + mappend = (<>) + mempty = CondNode mempty mempty mempty + -- | A 'CondBranch' represents a conditional branch, e.g., @if -- flag(foo)@ on some syntax @a@. It also has an optional false -- branch. @@ -191,4 +198,4 @@ foldCondTree e u mergeInclusive mergeExclusive = goTree goTree :: CondTree v c a -> b goTree (CondNode a c ifs) = u (c, a) `mergeInclusive` foldl goBranch e ifs goBranch :: b -> CondBranch v c a -> b - goBranch acc (CondBranch _ t mt) = mergeInclusive acc (maybe (goTree t) (mergeExclusive (goTree t) . goTree) mt) + goBranch acc (CondBranch _ t mt) = mergeInclusive acc (maybe (goTree t) (mergeExclusive (goTree t) . goTree) mt) \ No newline at end of file diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 037e40f1f15..96d8d26feca 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -50,13 +50,13 @@ import Distribution.Package import Distribution.Types.SourceRepo (RepoType) import Distribution.Types.CondTree (CondTree (..), CondBranch (..), condIfThen, condIfThenElse, mapTreeConds) import Distribution.PackageDescription - ( dispFlagAssignment, Condition (..), ConfVar (..), FlagAssignment (..) ) + ( dispFlagAssignment, Condition (..), ConfVar (..), FlagAssignment) import Distribution.PackageDescription.Configuration (simplifyWithSysParams) import Distribution.Simple.Compiler ( OptimisationLevel(..), DebugInfoLevel(..), CompilerInfo(..) ) import Distribution.Simple.InstallDirs ( CopyDest (NoCopyDest) ) import Distribution.Simple.Setup - ( Flag(Flag), toFlag, fromFlagOrDefault + ( Flag(..), toFlag, fromFlagOrDefault , ConfigFlags(..), configureOptions , HaddockFlags(..), haddockOptions, defaultHaddockFlags , TestFlags(..), testOptions', defaultTestFlags @@ -86,12 +86,14 @@ import Distribution.Deprecated.ReadP import qualified Text.PrettyPrint as Disp import Text.PrettyPrint ( Doc, ($+$) ) +import qualified Text.Parsec.Error as P +import qualified Text.Parsec.Pos as P import qualified Distribution.Deprecated.ParseUtils as ParseUtils import Distribution.Deprecated.ParseUtils ( ParseResult(..), PError(..), syntaxError, PWarning(..) , commaNewLineListFieldParsec, newLineListField, parseTokenQ , parseHaskellString, showToken - , simpleFieldParsec + , simpleFieldParsec, parseFail ) import Distribution.Client.ParseUtils import Distribution.Simple.Command @@ -99,18 +101,19 @@ import Distribution.Simple.Command , OptionField, option, reqArg' ) import Distribution.Types.PackageVersionConstraint ( PackageVersionConstraint ) -import Distribution.Parsec (ParsecParser, zeroPos) -import Distribution.System (Platform (..), OS, Arch) +import Distribution.Parsec (ParsecParser) +import Distribution.System (OS, Arch) import qualified Data.Map as Map import qualified Data.Set as Set import qualified Data.ByteString.Char8 as BS -import Network.URI (URI (..)) +import Network.URI (URI (..), parseURI) import Distribution.Fields.ConfVar (parseConditionConfVarFromClause) -import qualified Distribution.Fields.ParseResult as FPR -import Distribution.Fields.Field (SectionArg (..)) + +import Distribution.Client.HttpUtils +import System.FilePath (()) @@ -125,18 +128,11 @@ type ProjectConfigSkeleton = CondTree ConfVar [ProjectConfigImport] ProjectConfi type ProjectConfigImport = String -instance Semigroup (CondTree ConfVar [ProjectConfigImport] ProjectConfig) where - (CondNode a c bs) <> (CondNode a' c' bs') = CondNode (a <> a') (c <> c') (bs <> bs') - -instance Monoid (CondTree ConfVar [ProjectConfigImport] ProjectConfig) where - mappend = (<>) - mempty = CondNode mempty mempty mempty - singletonProjectConfigSkeleton :: ProjectConfig -> ProjectConfigSkeleton singletonProjectConfigSkeleton x = CondNode x mempty mempty instantiateProjectConfigSkeleton :: OS -> Arch -> CompilerInfo -> FlagAssignment -> ProjectConfigSkeleton -> ProjectConfig -instantiateProjectConfigSkeleton os arch impl flags skel = go $ mapTreeConds (fst . simplifyWithSysParams os arch impl) skel -- TODO bail on flags +instantiateProjectConfigSkeleton os arch impl _flags skel = go $ mapTreeConds (fst . simplifyWithSysParams os arch impl) skel where go :: CondTree FlagName @@ -149,24 +145,21 @@ instantiateProjectConfigSkeleton os arch impl flags skel = go $ mapTreeConds (fs processBranch (CondBranch cnd t mf) = case cnd of (Lit True) -> [go t] (Lit False) -> maybe ([]) ((:[]) . go) mf - _ -> [] - - --- TODO ensure parse doesn't have flags setting compiler inside conditionals + _ -> error $ "unable to process condition: " ++ show cnd -- TODO it would be nice if there were a pretty printer -- NOTE a nice refactor would be to use readFields directly to get a tree structure. -parseProjectSkeleton :: FilePath -> BS.ByteString -> IO (ParseResult ProjectConfigSkeleton) -parseProjectSkeleton source bs = runInnerParsers <$> linesToNode (BS.lines bs) +parseProjectSkeleton :: FilePath -> HttpTransport -> Verbosity -> FilePath -> BS.ByteString -> IO (ParseResult ProjectConfigSkeleton) +parseProjectSkeleton cacheDir httpTransport verbosity source bs = (>>= sanityWalkPCS False) . runInnerParsers <$> linesToNode (BS.lines bs) where - linesToNode :: [BS.ByteString] -> IO (CondTree ConfVar [ProjectConfigImport] [BS.ByteString]) + linesToNode :: [BS.ByteString] -> IO (CondTree BS.ByteString [ProjectConfigImport] [BS.ByteString]) linesToNode ls = packResult . mconcat <$> go ls - packResult :: ([CondBranch ConfVar [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString]) -> CondTree ConfVar [ProjectConfigImport] [BS.ByteString] + packResult :: ([CondBranch BS.ByteString [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString]) -> CondTree BS.ByteString [ProjectConfigImport] [BS.ByteString] packResult (branches, imps, ls) = CondNode ls imps branches - go :: [BS.ByteString] -> IO [([CondBranch ConfVar [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString])] + go :: [BS.ByteString] -> IO [([CondBranch BS.ByteString [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString])] go (l:ls) - | Just condition <- parseCond l = + | Just condition <- Var <$> detectCond l = let (clause, rest) = splitTillIndented ls in case rest of (r:rs) | (BS.pack "else") `BS.isPrefixOf` r -> -- TODO handle elif @@ -185,29 +178,59 @@ parseProjectSkeleton source bs = runInnerParsers <$> linesToNode (BS.lines bs) splitTillIndented = span ((BS.pack " ") `BS.isPrefixOf`) - parseCond :: BS.ByteString -> Maybe (Condition ConfVar) - parseCond l | (BS.pack "if(") `BS.isPrefixOf` l = case parseConditionConfVarFromClause l of - Left err -> error (show err) -- TODO improve error reporting here - Right x -> Just x + detectCond :: BS.ByteString -> Maybe BS.ByteString + detectCond l | (BS.pack "if(") `BS.isPrefixOf` l = Just l | otherwise = Nothing parseImport l | (BS.pack "import ") `BS.isPrefixOf` l = Just . BS.unpack $ BS.drop (length "import ") l | otherwise = Nothing - runInnerParsers :: CondTree ConfVar [ProjectConfigImport] [BS.ByteString] -> ParseResult ProjectConfigSkeleton - runInnerParsers = traverse (fmap (addProvenance . convertLegacyProjectConfig) . parseLegacyProjectConfig source . BS.unlines) + runInnerParsers :: CondTree BS.ByteString [ProjectConfigImport] [BS.ByteString] -> ParseResult ProjectConfigSkeleton + runInnerParsers = (runConditionParsers =<<) . traverse (fmap (addProvenance . convertLegacyProjectConfig) . parseLegacyProjectConfig source . BS.unlines) + + runConditionParsers :: (CondTree BS.ByteString [ProjectConfigImport] ProjectConfig) -> ParseResult ProjectConfigSkeleton + runConditionParsers (CondNode d c x) = CondNode d c <$> mapM runBranchConditionParser x + + runBranchConditionParser :: CondBranch BS.ByteString [ProjectConfigImport] ProjectConfig -> ParseResult (CondBranch ConfVar [ProjectConfigImport] ProjectConfig) + runBranchConditionParser (CondBranch (Var b) t f) = do + c <- adaptParseError $ parseConditionConfVarFromClause b -- nb this loses the source line location, can't win 'em all, sigh. A full refactor of parsers is the "right" way to fix this. + CondBranch c <$> runConditionParsers t <*> traverse runConditionParsers f + runBranchConditionParser (CondBranch x _ _) = error $ "internal cabal invariant error in parsing branch conditions: " ++ show x + + adaptParseError :: Either P.ParseError a -> ParseResult a + adaptParseError (Right x) = pure x + adaptParseError (Left e) = parseFail $ ParseUtils.NoParse (show e) (P.sourceLine $ P.errorPos e) addProvenance x = x {projectConfigProvenance = Set.singleton (Explicit source)} + modifiesCompiler :: ProjectConfig -> Bool + modifiesCompiler pc = isSet projectConfigHcFlavor || isSet projectConfigHcPath || isSet projectConfigHcPkg + where + isSet f = f (projectConfigShared pc) == NoFlag + + sanityWalkPCS :: Bool -> ProjectConfigSkeleton -> ParseResult ProjectConfigSkeleton + sanityWalkPCS underConditional t@(CondNode d _c comps) + | underConditional && modifiesCompiler d = parseFail $ ParseUtils.NoParse "Cannot set compiler in a conditional clause of a cabal project file" 0 + | otherwise = mapM_ sanityWalkBranch comps >> pure t + + sanityWalkBranch:: CondBranch ConfVar [ProjectConfigImport] ProjectConfig -> ParseResult () + sanityWalkBranch (CondBranch _c t f) = traverse (sanityWalkPCS True) f >> sanityWalkPCS True t >> pure () + fetchImportConfig :: ProjectConfigImport -> IO BS.ByteString - fetchImportConfig = BS.readFile -- todo, handle http + fetchImportConfig pci = case parseURI pci of + Just uri -> do + let fp = cacheDir show uri + _ <- downloadURI httpTransport verbosity uri fp + BS.readFile fp + Nothing -> BS.readFile pci + {- --- todo handlehttp -- todo add extra files to file change monitor - -- TODO handle importing legacy freeze as well -- TODO handle merge semantics for constraints specially +-- TODO somehow handle .local specially to avoid reconfig issues. + -} ------------------------------------------------------------------ From 912205c9d743ccf2a557d5f521315dc49046ef21 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Mon, 3 Jan 2022 16:51:52 -0500 Subject: [PATCH 06/22] thread through http transport --- .../src/Distribution/Client/ProjectConfig.hs | 16 +++++++++------- .../Distribution/Client/ProjectOrchestration.hs | 11 +++++++++-- .../src/Distribution/Client/ProjectPlanning.hs | 5 ++++- cabal-install/tests/IntegrationTests2.hs | 4 ++++ 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index c72f79bd9dd..5571cc02886 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -502,12 +502,13 @@ withProjectOrGlobalConfig' verbosity globalConfigFlag with without = do -- file if any, plus other global config. -- readProjectConfig :: Verbosity + -> HttpTransport -> Flag FilePath -> DistDirLayout -> Rebuild ProjectConfigSkeleton -readProjectConfig verbosity configFileFlag distDirLayout = do +readProjectConfig verbosity httpTransport configFileFlag distDirLayout = do global <- singletonProjectConfigSkeleton <$> readGlobalConfig verbosity configFileFlag - local <- readProjectLocalConfigOrDefault verbosity distDirLayout + local <- readProjectLocalConfigOrDefault verbosity httpTransport distDirLayout freeze <- singletonProjectConfigSkeleton <$> readProjectLocalFreezeConfig verbosity distDirLayout extra <- singletonProjectConfigSkeleton <$> readProjectLocalExtraConfig verbosity distDirLayout return (global <> local <> freeze <> extra) @@ -517,13 +518,14 @@ readProjectConfig verbosity configFileFlag distDirLayout = do -- or returns the default project config for an implicitly defined project. -- readProjectLocalConfigOrDefault :: Verbosity + -> HttpTransport -> DistDirLayout -> Rebuild ProjectConfigSkeleton -readProjectLocalConfigOrDefault verbosity distDirLayout = do +readProjectLocalConfigOrDefault verbosity httpTransport distDirLayout = do usesExplicitProjectRoot <- liftIO $ doesFileExist projectFile if usesExplicitProjectRoot then do - readProjectFileSkeleton verbosity distDirLayout "" "project file" + readProjectFileSkeleton verbosity httpTransport distDirLayout "" "project file" else do monitorFiles [monitorNonExistentFile projectFile] return (singletonProjectConfigSkeleton defaultImplicitProjectConfig) @@ -563,8 +565,8 @@ readProjectLocalFreezeConfig verbosity distDirLayout = -- | Reads a named extended (with imports and conditionals) config file in the given project root dir, or returns empty. -- -readProjectFileSkeleton :: Verbosity -> DistDirLayout -> String -> String -> Rebuild ProjectConfigSkeleton -readProjectFileSkeleton verbosity DistDirLayout{distProjectFile} +readProjectFileSkeleton :: Verbosity -> HttpTransport -> DistDirLayout -> String -> String -> Rebuild ProjectConfigSkeleton +readProjectFileSkeleton verbosity httpTransport DistDirLayout{distProjectFile, distDownloadSrcDirectory} extensionName extensionDescription = do exists <- liftIO $ doesFileExist extensionFile if exists @@ -577,7 +579,7 @@ readProjectFileSkeleton verbosity DistDirLayout{distProjectFile} readExtensionFile = reportParseResult verbosity extensionDescription extensionFile - =<< parseProjectSkeleton extensionFile + =<< parseProjectSkeleton distDownloadSrcDirectory httpTransport verbosity extensionFile =<< BS.readFile extensionFile -- | Reads a named config file in the given project root dir, or returns empty. diff --git a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs index d4de014a7de..d7933b6c2a9 100644 --- a/cabal-install/src/Distribution/Client/ProjectOrchestration.hs +++ b/cabal-install/src/Distribution/Client/ProjectOrchestration.hs @@ -139,6 +139,7 @@ import qualified Distribution.Client.BuildReports.Storage as BuildReports ( storeLocal ) import Distribution.Client.Config (getCabalDir) +import Distribution.Client.HttpUtils import Distribution.Client.Setup hiding (packageName) import Distribution.Compiler ( CompilerFlavor(GHC) ) @@ -170,7 +171,8 @@ import Distribution.Version import Distribution.Simple.Compiler ( compilerCompatVersion, showCompilerId, compilerId, compilerInfo , OptimisationLevel(..)) - +import Distribution.Utils.NubList + ( fromNubList ) import Distribution.System ( Platform(Platform) ) @@ -210,7 +212,7 @@ establishProjectBaseContext verbosity cliConfig currentCommand = do establishProjectBaseContextWithRoot verbosity cliConfig projectRoot currentCommand where mprojectFile = Setup.flagToMaybe projectConfigProjectFile - ProjectConfigShared { projectConfigProjectFile } = projectConfigShared cliConfig + ProjectConfigShared { projectConfigProjectFile} = projectConfigShared cliConfig -- | Like 'establishProjectBaseContext' but doesn't search for project root. establishProjectBaseContextWithRoot @@ -224,8 +226,13 @@ establishProjectBaseContextWithRoot verbosity cliConfig projectRoot currentComma let distDirLayout = defaultDistDirLayout projectRoot mdistDirectory + httpTransport <- configureTransport verbosity + (fromNubList . projectConfigProgPathExtra $ projectConfigShared cliConfig) + (flagToMaybe . projectConfigHttpTransport $ projectConfigBuildOnly cliConfig) + (projectConfig, localPackages) <- rebuildProjectConfig verbosity + httpTransport distDirLayout cliConfig diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 7b5e23434d6..628dc7e9f40 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -71,6 +71,7 @@ import Prelude () import Distribution.Client.Compat.Prelude import Distribution.Client.HashValue +import Distribution.Client.HttpUtils import Distribution.Client.ProjectPlanning.Types as Ty import Distribution.Client.PackageHash import Distribution.Client.RebuildMonad @@ -301,11 +302,13 @@ sanityCheckElaboratedPackage ElaboratedConfiguredPackage{..} -- packages within the project. -- rebuildProjectConfig :: Verbosity + -> HttpTransport -> DistDirLayout -> ProjectConfig -> IO ( ProjectConfig , [PackageSpecifier UnresolvedSourcePackage] ) rebuildProjectConfig verbosity + httpTransport distDirLayout@DistDirLayout { distProjectRootDirectory, distDirectory, @@ -360,7 +363,7 @@ rebuildProjectConfig verbosity -- phaseReadProjectConfig :: Rebuild ProjectConfigSkeleton phaseReadProjectConfig = do - readProjectConfig verbosity projectConfigConfigFile distDirLayout + readProjectConfig verbosity httpTransport projectConfigConfigFile distDirLayout -- Look for all the cabal packages in the project -- some of which may be local src dirs, tarballs etc diff --git a/cabal-install/tests/IntegrationTests2.hs b/cabal-install/tests/IntegrationTests2.hs index 4ca346930ed..700c4342c22 100644 --- a/cabal-install/tests/IntegrationTests2.hs +++ b/cabal-install/tests/IntegrationTests2.hs @@ -16,6 +16,7 @@ import Prelude () import Distribution.Client.DistDirLayout import Distribution.Client.ProjectConfig import Distribution.Client.Config (getCabalDir) +import Distribution.Client.HttpUtils import Distribution.Client.TargetSelector hiding (DirActions(..)) import qualified Distribution.Client.TargetSelector as TS (DirActions(..)) import Distribution.Client.ProjectPlanning @@ -1511,8 +1512,11 @@ configureProject testdir cliConfig = do -- ended in an exception (as we leave the files to help with debugging). cleanProject testdir + httpTransport <- configureTransport verbosity [] Nothing + (projectConfig, localPackages) <- rebuildProjectConfig verbosity + httpTransport distDirLayout cliConfig From e0607be72b96e0216b4624d40333837ae62bc863 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Mon, 3 Jan 2022 17:27:46 -0500 Subject: [PATCH 07/22] fix merge --- .../src/Distribution/Deprecated/ParseUtils.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cabal-install/src/Distribution/Deprecated/ParseUtils.hs b/cabal-install/src/Distribution/Deprecated/ParseUtils.hs index c1a0e4cd30c..6ac62a6e82d 100644 --- a/cabal-install/src/Distribution/Deprecated/ParseUtils.hs +++ b/cabal-install/src/Distribution/Deprecated/ParseUtils.hs @@ -105,6 +105,11 @@ instance Monad ParseResult where ParseOk ws x >>= f = case f x of ParseFailed err -> ParseFailed err ParseOk ws' x' -> ParseOk (ws'++ws) x' +#if !(MIN_VERSION_base(4,9,0)) + fail = parseResultFail +#elif !(MIN_VERSION_base(4,13,0)) + fail = Fail.fail +#endif instance Foldable ParseResult where foldMap _ (ParseFailed _ ) = mempty @@ -114,11 +119,6 @@ instance Traversable ParseResult where traverse _ (ParseFailed err) = pure (ParseFailed err) traverse f (ParseOk ws x) = ParseOk ws <$> f x -#if !(MIN_VERSION_base(4,9,0)) - fail = parseResultFail -#elif !(MIN_VERSION_base(4,13,0)) - fail = Fail.fail -#endif instance Fail.MonadFail ParseResult where fail = parseResultFail From ec3763fc871e4bb16140cbc418ba72e637981813 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Wed, 5 Jan 2022 15:59:03 -0500 Subject: [PATCH 08/22] better errors and use extended project parsing uniformly --- Cabal/src/Distribution/Types/CondTree.hs | 2 +- .../src/Distribution/Client/CmdConfigure.hs | 17 +++-- .../src/Distribution/Client/CmdOutdated.hs | 38 +++++++---- .../src/Distribution/Client/ProjectConfig.hs | 64 ++++--------------- .../Client/ProjectConfig/Legacy.hs | 31 +++++---- .../Distribution/Client/ProjectPlanning.hs | 1 + 6 files changed, 71 insertions(+), 82 deletions(-) diff --git a/Cabal/src/Distribution/Types/CondTree.hs b/Cabal/src/Distribution/Types/CondTree.hs index 442e4b48b8c..8f1d17a8163 100644 --- a/Cabal/src/Distribution/Types/CondTree.hs +++ b/Cabal/src/Distribution/Types/CondTree.hs @@ -71,7 +71,7 @@ instance (NFData v, NFData c, NFData a) => NFData (CondTree v c a) where rnf = g instance (Semigroup a, Semigroup c) => Semigroup (CondTree v c a) where (CondNode a c bs) <> (CondNode a' c' bs') = CondNode (a <> a') (c <> c') (bs <> bs') -instance (Monoid a, Monoid c) => Monoid (CondTree v c a) where +instance (Semigroup a, Semigroup c, Monoid a, Monoid c) => Monoid (CondTree v c a) where mappend = (<>) mempty = CondNode mempty mempty mempty diff --git a/cabal-install/src/Distribution/Client/CmdConfigure.hs b/cabal-install/src/Distribution/Client/CmdConfigure.hs index f734f15c668..cfe4741d0bd 100644 --- a/cabal-install/src/Distribution/Client/CmdConfigure.hs +++ b/cabal-install/src/Distribution/Client/CmdConfigure.hs @@ -30,12 +30,17 @@ import Distribution.Verbosity import Distribution.Simple.Command ( CommandUI(..), usageAlternatives ) import Distribution.Simple.Utils - ( wrapText, notice ) + ( wrapText, notice, die' ) import Distribution.Client.DistDirLayout ( DistDirLayout(..) ) import Distribution.Client.RebuildMonad (runRebuild) import Distribution.Client.ProjectConfig.Types +import Distribution.Client.HttpUtils +import Distribution.Utils.NubList + ( fromNubList ) +import Distribution.Types.CondTree + ( CondTree (..) ) configureCommand :: CommandUI (NixStyleFlags ()) configureCommand = CommandUI { @@ -126,9 +131,13 @@ configureAction' flags@NixStyleFlags {..} _extraArgs globalFlags = do -- If the flag @configAppend@ is set to true, append and do not overwrite if exists && appends then do - conf <- runRebuild (distProjectRootDirectory . distDirLayout $ baseCtx) $ - readProjectLocalExtraConfig v (distDirLayout baseCtx) - return (baseCtx, conf <> cliConfig) -- TODO ugh. maybe don't have extra configs have conditions, only main configs + httpTransport <- configureTransport v + (fromNubList . projectConfigProgPathExtra $ projectConfigShared cliConfig) + (flagToMaybe . projectConfigHttpTransport $ projectConfigBuildOnly cliConfig) + (CondNode conf imps bs) <- runRebuild (distProjectRootDirectory . distDirLayout $ baseCtx) $ + readProjectLocalExtraConfig v httpTransport (distDirLayout baseCtx) + when (not (null imps && null bs)) $ die' v "local project file has conditional and/or import logic, unable to perform and automatic in-place update" + return (baseCtx, conf <> cliConfig) else return (baseCtx, cliConfig) where diff --git a/cabal-install/src/Distribution/Client/CmdOutdated.hs b/cabal-install/src/Distribution/Client/CmdOutdated.hs index b2b817a00ce..d09fdabe900 100644 --- a/cabal-install/src/Distribution/Client/CmdOutdated.hs +++ b/cabal-install/src/Distribution/Client/CmdOutdated.hs @@ -29,9 +29,10 @@ import Distribution.Client.DistDirLayout ( defaultDistDirLayout , DistDirLayout(distProjectRootDirectory, distProjectFile) ) import Distribution.Client.ProjectConfig - ( ProjectConfig(projectConfigShared), - ProjectConfigShared(projectConfigConstraints), findProjectRoot, - readProjectLocalFreezeConfig ) +import Distribution.Client.ProjectConfig.Legacy + ( instantiateProjectConfigSkeleton ) +import Distribution.Client.ProjectPlanning + ( configureCompiler ) import Distribution.Client.ProjectFlags ( projectFlagsOptions, ProjectFlags(..), defaultProjectFlags , removeIgnoreProjectOption ) @@ -40,8 +41,6 @@ import Distribution.Client.RebuildMonad import Distribution.Client.Sandbox ( loadConfigOrSandboxConfig ) import Distribution.Client.Setup - ( withRepoContext, GlobalFlags, configCompilerAux' - , ConfigExFlags(configExConstraints) ) import Distribution.Client.Targets ( userToPackageConstraint, UserConstraint ) import Distribution.Client.Types.SourcePackageDb as SourcePackageDb @@ -55,7 +54,7 @@ import Distribution.Utils.Generic import Distribution.Package ( PackageName, packageVersion ) import Distribution.PackageDescription - ( allBuildDepends ) + ( allBuildDepends, ignoreConditions ) import Distribution.PackageDescription.Configuration ( finalizePD ) import Distribution.Simple.Compiler @@ -65,7 +64,7 @@ import Distribution.Simple.Setup import Distribution.Simple.Utils ( die', notice, debug, tryFindPackageDesc ) import Distribution.System - ( Platform ) + ( Platform (..) ) import Distribution.Types.ComponentRequestedSpec ( ComponentRequestedSpec(..) ) import Distribution.Types.Dependency @@ -86,6 +85,10 @@ import Distribution.Simple.Command import qualified Distribution.Compat.CharParsing as P import Distribution.ReadE ( parsecToReadE ) +import Distribution.Client.HttpUtils +import Distribution.Utils.NubList + ( fromNubList ) + import qualified Data.Set as S import System.Directory @@ -220,7 +223,6 @@ outdatedAction (ProjectFlags{flagProjectFileName}, OutdatedFlags{..}) _targetStr config <- loadConfigOrSandboxConfig verbosity globalFlags let globalFlags' = savedGlobalFlags config `mappend` globalFlags configFlags = savedConfigureFlags config - (comp, platform, _progdb) <- configCompilerAux' configFlags withRepoContext verbosity globalFlags' $ \repoContext -> do when (not newFreezeFile && isJust mprojectFile) $ die' verbosity $ @@ -230,8 +232,14 @@ outdatedAction (ProjectFlags{flagProjectFileName}, OutdatedFlags{..}) _targetStr deps <- if freezeFile then depsFromFreezeFile verbosity else if newFreezeFile - then depsFromNewFreezeFile verbosity mprojectFile - else depsFromPkgDesc verbosity comp platform + then do + httpTransport <- configureTransport verbosity + (fromNubList . globalProgPathExtra $ globalFlags) + (flagToMaybe . globalHttpTransport $ globalFlags) + depsFromNewFreezeFile verbosity httpTransport mprojectFile + else do + (comp, platform, _progdb) <- configCompilerAux' configFlags + depsFromPkgDesc verbosity comp platform debug verbosity $ "Dependencies loaded: " ++ intercalate ", " (map prettyShow deps) let outdatedDeps = listOutdated deps sourcePkgDb @@ -293,14 +301,16 @@ depsFromFreezeFile verbosity = do return deps -- | Read the list of dependencies from the new-style freeze file. -depsFromNewFreezeFile :: Verbosity -> Maybe FilePath -> IO [PackageVersionConstraint] -depsFromNewFreezeFile verbosity mprojectFile = do +depsFromNewFreezeFile :: Verbosity -> HttpTransport -> Maybe FilePath -> IO [PackageVersionConstraint] +depsFromNewFreezeFile verbosity httpTransport mprojectFile = do projectRoot <- either throwIO return =<< findProjectRoot Nothing mprojectFile let distDirLayout = defaultDistDirLayout projectRoot {- TODO: Support dist dir override -} Nothing - projectConfig <- runRebuild (distProjectRootDirectory distDirLayout) $ - readProjectLocalFreezeConfig verbosity distDirLayout + projectConfig <- runRebuild (distProjectRootDirectory distDirLayout) $ do + pcs <- readProjectLocalFreezeConfig verbosity httpTransport distDirLayout + (compiler, Platform arch os, _) <- configureCompiler verbosity distDirLayout (fst $ ignoreConditions pcs) + pure $ instantiateProjectConfigSkeleton os arch (compilerInfo compiler) mempty pcs let ucnstrs = map fst . projectConfigConstraints . projectConfigShared $ projectConfig deps = userConstraintsToDependencies ucnstrs diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index f879c61d5e8..c1811902c3d 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -507,10 +507,10 @@ readProjectConfig :: Verbosity -> DistDirLayout -> Rebuild ProjectConfigSkeleton readProjectConfig verbosity httpTransport configFileFlag distDirLayout = do - global <- singletonProjectConfigSkeleton <$> readGlobalConfig verbosity configFileFlag + global <- singletonProjectConfigSkeleton <$> readGlobalConfig verbosity configFileFlag local <- readProjectLocalConfigOrDefault verbosity httpTransport distDirLayout - freeze <- singletonProjectConfigSkeleton <$> readProjectLocalFreezeConfig verbosity distDirLayout - extra <- singletonProjectConfigSkeleton <$> readProjectLocalExtraConfig verbosity distDirLayout + freeze <- readProjectLocalFreezeConfig verbosity httpTransport distDirLayout + extra <- readProjectLocalExtraConfig verbosity httpTransport distDirLayout return (global <> local <> freeze <> extra) @@ -547,20 +547,20 @@ readProjectLocalConfigOrDefault verbosity httpTransport distDirLayout = do -- or returns empty. This file gets written by @cabal configure@, or in -- principle can be edited manually or by other tools. -- -readProjectLocalExtraConfig :: Verbosity -> DistDirLayout - -> Rebuild ProjectConfig -readProjectLocalExtraConfig verbosity distDirLayout = - readProjectFile verbosity distDirLayout "local" +readProjectLocalExtraConfig :: Verbosity -> HttpTransport -> DistDirLayout + -> Rebuild ProjectConfigSkeleton +readProjectLocalExtraConfig verbosity httpTransport distDirLayout = + readProjectFileSkeleton verbosity httpTransport distDirLayout "local" "project local configuration file" -- | Reads a @cabal.project.freeze@ file in the given project root dir, -- or returns empty. This file gets written by @cabal freeze@, or in -- principle can be edited manually or by other tools. -- -readProjectLocalFreezeConfig :: Verbosity -> DistDirLayout - -> Rebuild ProjectConfig -readProjectLocalFreezeConfig verbosity distDirLayout = - readProjectFile verbosity distDirLayout "freeze" +readProjectLocalFreezeConfig :: Verbosity -> HttpTransport ->DistDirLayout + -> Rebuild ProjectConfigSkeleton +readProjectLocalFreezeConfig verbosity httpTransport distDirLayout = + readProjectFileSkeleton verbosity httpTransport distDirLayout "freeze" "project freeze file" -- | Reads a named extended (with imports and conditionals) config file in the given project root dir, or returns empty. @@ -571,7 +571,9 @@ readProjectFileSkeleton verbosity httpTransport DistDirLayout{distProjectFile, d exists <- liftIO $ doesFileExist extensionFile if exists then do monitorFiles [monitorFileHashed extensionFile] - liftIO readExtensionFile + pcs <- liftIO readExtensionFile + monitorFiles $ map monitorFileHashed (projectSkeletonImports pcs) + pure pcs else do monitorFiles [monitorNonExistentFile extensionFile] return mempty where @@ -582,44 +584,6 @@ readProjectFileSkeleton verbosity httpTransport DistDirLayout{distProjectFile, d =<< parseProjectSkeleton distDownloadSrcDirectory httpTransport verbosity extensionFile =<< BS.readFile extensionFile --- | Reads a named config file in the given project root dir, or returns empty. --- -readProjectFile :: Verbosity -> DistDirLayout -> String -> String -> Rebuild ProjectConfig -readProjectFile verbosity DistDirLayout{distProjectFile} - extensionName extensionDescription = do - exists <- liftIO $ doesFileExist extensionFile - if exists - then do monitorFiles [monitorFileHashed extensionFile] - addProjectFileProvenance <$> liftIO readExtensionFile - else do monitorFiles [monitorNonExistentFile extensionFile] - return mempty - where - extensionFile :: FilePath - extensionFile = distProjectFile extensionName - - readExtensionFile :: IO ProjectConfig - readExtensionFile = - reportParseResult verbosity extensionDescription extensionFile - . (parseProjectConfig extensionFile) - =<< BS.readFile extensionFile - - addProjectFileProvenance :: ProjectConfig -> ProjectConfig - addProjectFileProvenance config = - config { - projectConfigProvenance = Set.insert (Explicit extensionFile) (projectConfigProvenance config) - } - - --- | Parse the 'ProjectConfig' format. --- --- For the moment this is implemented in terms of parsers for legacy --- configuration types, plus a conversion. --- -parseProjectConfig :: FilePath -> BS.ByteString -> OldParser.ParseResult ProjectConfig -parseProjectConfig source content = - convertLegacyProjectConfig <$> - (parseLegacyProjectConfig source content) - -- | Render the 'ProjectConfig' format. -- -- For the moment this is implemented in terms of a pretty printer for the diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 96d8d26feca..7d3d084beb5 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -9,6 +9,7 @@ module Distribution.Client.ProjectConfig.Legacy ( parseProjectSkeleton, instantiateProjectConfigSkeleton, singletonProjectConfigSkeleton, + projectSkeletonImports, -- * Project config in terms of legacy types LegacyProjectConfig, @@ -43,14 +44,17 @@ import Distribution.Client.CmdInstall.ClientInstallFlags ( ClientInstallFlags(..), defaultClientInstallFlags , clientInstallOptions ) +import Distribution.Compat.Lens (view) + import Distribution.Solver.Types.ConstraintSource import Distribution.FieldGrammar import Distribution.Package import Distribution.Types.SourceRepo (RepoType) -import Distribution.Types.CondTree (CondTree (..), CondBranch (..), condIfThen, condIfThenElse, mapTreeConds) +import Distribution.Types.CondTree + ( CondTree (..), CondBranch (..), condIfThen, condIfThenElse, mapTreeConds, traverseCondTreeC ) import Distribution.PackageDescription - ( dispFlagAssignment, Condition (..), ConfVar (..), FlagAssignment) + ( dispFlagAssignment, Condition (..), ConfVar (..), FlagAssignment ) import Distribution.PackageDescription.Configuration (simplifyWithSysParams) import Distribution.Simple.Compiler ( OptimisationLevel(..), DebugInfoLevel(..), CompilerInfo(..) ) @@ -113,7 +117,8 @@ import Network.URI (URI (..), parseURI) import Distribution.Fields.ConfVar (parseConditionConfVarFromClause) import Distribution.Client.HttpUtils -import System.FilePath (()) +import System.FilePath ((), isPathSeparator) +import System.Directory (createDirectoryIfMissing) @@ -147,6 +152,9 @@ instantiateProjectConfigSkeleton os arch impl _flags skel = go $ mapTreeConds (f (Lit False) -> maybe ([]) ((:[]) . go) mf _ -> error $ "unable to process condition: " ++ show cnd -- TODO it would be nice if there were a pretty printer +projectSkeletonImports :: ProjectConfigSkeleton -> [ProjectConfigImport] +projectSkeletonImports = view traverseCondTreeC + -- NOTE a nice refactor would be to use readFields directly to get a tree structure. parseProjectSkeleton :: FilePath -> HttpTransport -> Verbosity -> FilePath -> BS.ByteString -> IO (ParseResult ProjectConfigSkeleton) parseProjectSkeleton cacheDir httpTransport verbosity source bs = (>>= sanityWalkPCS False) . runInnerParsers <$> linesToNode (BS.lines bs) @@ -192,24 +200,24 @@ parseProjectSkeleton cacheDir httpTransport verbosity source bs = (>>= sanityWal runBranchConditionParser :: CondBranch BS.ByteString [ProjectConfigImport] ProjectConfig -> ParseResult (CondBranch ConfVar [ProjectConfigImport] ProjectConfig) runBranchConditionParser (CondBranch (Var b) t f) = do - c <- adaptParseError $ parseConditionConfVarFromClause b -- nb this loses the source line location, can't win 'em all, sigh. A full refactor of parsers is the "right" way to fix this. + c <- adaptParseError $ parseConditionConfVarFromClause b -- nb this loses the source line location, can't win 'em all, sigh. A full refactor of parsers is the "right" way to fix this. We could also pack bytestrings for conditionals with line locations CondBranch c <$> runConditionParsers t <*> traverse runConditionParsers f runBranchConditionParser (CondBranch x _ _) = error $ "internal cabal invariant error in parsing branch conditions: " ++ show x adaptParseError :: Either P.ParseError a -> ParseResult a adaptParseError (Right x) = pure x - adaptParseError (Left e) = parseFail $ ParseUtils.NoParse (show e) (P.sourceLine $ P.errorPos e) + adaptParseError (Left e) = parseFail $ ParseUtils.FromString (show e) (Just . P.sourceLine $ P.errorPos e) addProvenance x = x {projectConfigProvenance = Set.singleton (Explicit source)} modifiesCompiler :: ProjectConfig -> Bool modifiesCompiler pc = isSet projectConfigHcFlavor || isSet projectConfigHcPath || isSet projectConfigHcPkg where - isSet f = f (projectConfigShared pc) == NoFlag + isSet f = f (projectConfigShared pc) /= NoFlag sanityWalkPCS :: Bool -> ProjectConfigSkeleton -> ParseResult ProjectConfigSkeleton sanityWalkPCS underConditional t@(CondNode d _c comps) - | underConditional && modifiesCompiler d = parseFail $ ParseUtils.NoParse "Cannot set compiler in a conditional clause of a cabal project file" 0 + | underConditional && modifiesCompiler d = parseFail $ ParseUtils.FromString "Cannot set compiler in a conditional clause of a cabal project file" Nothing | otherwise = mapM_ sanityWalkBranch comps >> pure t sanityWalkBranch:: CondBranch ConfVar [ProjectConfigImport] ProjectConfig -> ParseResult () @@ -218,19 +226,16 @@ parseProjectSkeleton cacheDir httpTransport verbosity source bs = (>>= sanityWal fetchImportConfig :: ProjectConfigImport -> IO BS.ByteString fetchImportConfig pci = case parseURI pci of Just uri -> do - let fp = cacheDir show uri + let fp = cacheDir map (\x -> if isPathSeparator x then 'X' else x) (show uri) -- TODO can we do better? + createDirectoryIfMissing True cacheDir _ <- downloadURI httpTransport verbosity uri fp BS.readFile fp Nothing -> BS.readFile pci {- --- todo add extra files to file change monitor --- TODO handle importing legacy freeze as well +-- TODO elif -- TODO handle merge semantics for constraints specially - --- TODO somehow handle .local specially to avoid reconfig issues. - -} ------------------------------------------------------------------ diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 628dc7e9f40..3f05be33487 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -40,6 +40,7 @@ module Distribution.Client.ProjectPlanning ( -- * Utils required for building pkgHasEphemeralBuildTargets, elabBuildTargetWholeComponents, + configureCompiler, -- * Setup.hs CLI flags for building setupHsScriptOptions, From 21ebfc1a9f64fe19064c0026bac0651f57ade23d Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Wed, 5 Jan 2022 18:11:51 -0500 Subject: [PATCH 09/22] elif support, maybe? --- .../Client/ProjectConfig/Legacy.hs | 64 +++++++++++-------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 7d3d084beb5..4454a58615e 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -159,39 +159,55 @@ projectSkeletonImports = view traverseCondTreeC parseProjectSkeleton :: FilePath -> HttpTransport -> Verbosity -> FilePath -> BS.ByteString -> IO (ParseResult ProjectConfigSkeleton) parseProjectSkeleton cacheDir httpTransport verbosity source bs = (>>= sanityWalkPCS False) . runInnerParsers <$> linesToNode (BS.lines bs) where + -- converts lines to a full tree node, recursively looping "go" to pull out conditional and import structure, then packing the whole thing up linesToNode :: [BS.ByteString] -> IO (CondTree BS.ByteString [ProjectConfigImport] [BS.ByteString]) - linesToNode ls = packResult . mconcat <$> go ls - - packResult :: ([CondBranch BS.ByteString [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString]) -> CondTree BS.ByteString [ProjectConfigImport] [BS.ByteString] - packResult (branches, imps, ls) = CondNode ls imps branches + linesToNode xs = (\(branches, imps, ls) -> CondNode ls imps branches) . mconcat <$> go xs + -- given a list of lines, pulls out the conditional and import structure go :: [BS.ByteString] -> IO [([CondBranch BS.ByteString [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString])] go (l:ls) - | Just condition <- Var <$> detectCond l = - let (clause, rest) = splitTillIndented ls - in case rest of - (r:rs) | (BS.pack "else") `BS.isPrefixOf` r -> -- TODO handle elif - let (elseClause, lastRest) = splitTillIndented rs - in do - c1 <- linesToNode clause - c2 <- linesToNode elseClause - (([condIfThenElse condition c1 c2], [], []) :) <$> go lastRest - _ -> do - c1 <- linesToNode clause - (([condIfThen condition c1], [], []) :) <$> go rest + | (BS.pack "if(") `BS.isPrefixOf` l = + let (clause, rest) = splitWhileIndented ls + + -- unpacks the results of loop into nested if else clauses + constructNestedConds topCond topClause [] [] = + do c1 <- linesToNode topClause + pure $ condIfThen (Var topCond) c1 + constructNestedConds topCond topClause ((elifCond, elifClause):elifs) elseClause = + do c1 <- linesToNode topClause + condIfThenElse (Var topCond) c1 . CondNode [] [] . (:[]) <$> constructNestedConds elifCond elifClause elifs elseClause + constructNestedConds topCond topClause [] elseClause = + do c1 <- linesToNode topClause + c2 <- linesToNode elseClause + pure $ condIfThenElse (Var topCond) c1 c2 + + -- parse out the full list of if/else clauses + loop acc rss = + case rss of + (r:rs) + | BS.pack "elif" `BS.isPrefixOf` r -> + let (elseClause, lastRest) = splitWhileIndented rs + in loop ((r, elseClause):acc) lastRest + | BS.pack "else" `BS.isPrefixOf` r -> + let (elseClause, lastRest) = splitWhileIndented rs + in constructNestedConds l clause (reverse acc) elseClause + >>= (\c -> ((([c],[],[]) :) <$> go lastRest)) + _ -> constructNestedConds l clause (reverse acc) [] + >>= (\c -> ((([c],[],[]) :) <$> go rss)) + in loop [] rest + | Just imp <- parseImport l = do x <- go . BS.lines =<< fetchImportConfig imp ((([], [imp], []) : x) ++) <$> go ls + | otherwise = (([], [], [l]) :) <$> go ls - go [] = pure [] - splitTillIndented = span ((BS.pack " ") `BS.isPrefixOf`) + go [] = pure [] - detectCond :: BS.ByteString -> Maybe BS.ByteString - detectCond l | (BS.pack "if(") `BS.isPrefixOf` l = Just l + splitWhileIndented = span ((BS.pack " ") `BS.isPrefixOf`) - | otherwise = Nothing parseImport l | (BS.pack "import ") `BS.isPrefixOf` l = Just . BS.unpack $ BS.drop (length "import ") l | otherwise = Nothing + runInnerParsers :: CondTree BS.ByteString [ProjectConfigImport] [BS.ByteString] -> ParseResult ProjectConfigSkeleton runInnerParsers = (runConditionParsers =<<) . traverse (fmap (addProvenance . convertLegacyProjectConfig) . parseLegacyProjectConfig source . BS.unlines) @@ -232,12 +248,6 @@ parseProjectSkeleton cacheDir httpTransport verbosity source bs = (>>= sanityWal BS.readFile fp Nothing -> BS.readFile pci - -{- --- TODO elif --- TODO handle merge semantics for constraints specially --} - ------------------------------------------------------------------ -- Representing the project config file in terms of legacy types -- From bd0031eac9762a67dbbd1bc6205f3507a4c9a631 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Thu, 6 Jan 2022 17:07:37 -0500 Subject: [PATCH 10/22] fix outdated cmd, add tests, docs --- .../src/Distribution/Client/CmdOutdated.hs | 14 +++--- .../Distribution/Client/ProjectPlanning.hs | 48 ------------------- .../PackageTests/ConditionalAndImport/Foo.hs | 1 + .../ConditionalAndImport/cabal.out | 12 +++++ .../ConditionalAndImport/cabal.project | 3 ++ .../ConditionalAndImport/cabal.test.hs | 4 ++ .../ConditionalAndImport/extra.project | 8 ++++ .../ConditionalAndImport/my.cabal | 9 ++++ .../repo/some-exe-0.0.1.0/Main.hs | 4 ++ .../repo/some-exe-0.0.1.0/some-exe.cabal | 9 ++++ doc/cabal-project.rst | 25 ++++++++++ 11 files changed, 81 insertions(+), 56 deletions(-) create mode 100644 cabal-testsuite/PackageTests/ConditionalAndImport/Foo.hs create mode 100644 cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out create mode 100644 cabal-testsuite/PackageTests/ConditionalAndImport/cabal.project create mode 100644 cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs create mode 100644 cabal-testsuite/PackageTests/ConditionalAndImport/extra.project create mode 100644 cabal-testsuite/PackageTests/ConditionalAndImport/my.cabal create mode 100644 cabal-testsuite/PackageTests/ConditionalAndImport/repo/some-exe-0.0.1.0/Main.hs create mode 100644 cabal-testsuite/PackageTests/ConditionalAndImport/repo/some-exe-0.0.1.0/some-exe.cabal diff --git a/cabal-install/src/Distribution/Client/CmdOutdated.hs b/cabal-install/src/Distribution/Client/CmdOutdated.hs index d09fdabe900..7b9eb4a1c0a 100644 --- a/cabal-install/src/Distribution/Client/CmdOutdated.hs +++ b/cabal-install/src/Distribution/Client/CmdOutdated.hs @@ -31,8 +31,6 @@ import Distribution.Client.DistDirLayout import Distribution.Client.ProjectConfig import Distribution.Client.ProjectConfig.Legacy ( instantiateProjectConfigSkeleton ) -import Distribution.Client.ProjectPlanning - ( configureCompiler ) import Distribution.Client.ProjectFlags ( projectFlagsOptions, ProjectFlags(..), defaultProjectFlags , removeIgnoreProjectOption ) @@ -54,7 +52,7 @@ import Distribution.Utils.Generic import Distribution.Package ( PackageName, packageVersion ) import Distribution.PackageDescription - ( allBuildDepends, ignoreConditions ) + ( allBuildDepends ) import Distribution.PackageDescription.Configuration ( finalizePD ) import Distribution.Simple.Compiler @@ -94,6 +92,7 @@ import qualified Data.Set as S import System.Directory ( getCurrentDirectory, doesFileExist ) + ------------------------------------------------------------------------------- -- Command ------------------------------------------------------------------------------- @@ -229,6 +228,7 @@ outdatedAction (ProjectFlags{flagProjectFileName}, OutdatedFlags{..}) _targetStr "--project-file must only be used with --v2-freeze-file." sourcePkgDb <- IndexUtils.getSourcePackages verbosity repoContext + (comp, platform, _progdb) <- configCompilerAux' configFlags deps <- if freezeFile then depsFromFreezeFile verbosity else if newFreezeFile @@ -236,9 +236,8 @@ outdatedAction (ProjectFlags{flagProjectFileName}, OutdatedFlags{..}) _targetStr httpTransport <- configureTransport verbosity (fromNubList . globalProgPathExtra $ globalFlags) (flagToMaybe . globalHttpTransport $ globalFlags) - depsFromNewFreezeFile verbosity httpTransport mprojectFile + depsFromNewFreezeFile verbosity httpTransport comp platform mprojectFile else do - (comp, platform, _progdb) <- configCompilerAux' configFlags depsFromPkgDesc verbosity comp platform debug verbosity $ "Dependencies loaded: " ++ intercalate ", " (map prettyShow deps) @@ -301,15 +300,14 @@ depsFromFreezeFile verbosity = do return deps -- | Read the list of dependencies from the new-style freeze file. -depsFromNewFreezeFile :: Verbosity -> HttpTransport -> Maybe FilePath -> IO [PackageVersionConstraint] -depsFromNewFreezeFile verbosity httpTransport mprojectFile = do +depsFromNewFreezeFile :: Verbosity -> HttpTransport -> Compiler -> Platform -> Maybe FilePath -> IO [PackageVersionConstraint] +depsFromNewFreezeFile verbosity httpTransport compiler (Platform arch os) mprojectFile = do projectRoot <- either throwIO return =<< findProjectRoot Nothing mprojectFile let distDirLayout = defaultDistDirLayout projectRoot {- TODO: Support dist dir override -} Nothing projectConfig <- runRebuild (distProjectRootDirectory distDirLayout) $ do pcs <- readProjectLocalFreezeConfig verbosity httpTransport distDirLayout - (compiler, Platform arch os, _) <- configureCompiler verbosity distDirLayout (fst $ ignoreConditions pcs) pure $ instantiateProjectConfigSkeleton os arch (compilerInfo compiler) mempty pcs let ucnstrs = map fst . projectConfigConstraints . projectConfigShared $ projectConfig diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 3f05be33487..304f0f0e6ea 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -40,7 +40,6 @@ module Distribution.Client.ProjectPlanning ( -- * Utils required for building pkgHasEphemeralBuildTargets, elabBuildTargetWholeComponents, - configureCompiler, -- * Setup.hs CLI flags for building setupHsScriptOptions, @@ -534,53 +533,6 @@ rebuildInstallPlan verbosity phaseConfigureCompiler :: ProjectConfig -> Rebuild (Compiler, Platform, ProgramDb) phaseConfigureCompiler = configureCompiler verbosity distDirLayout -{- - ProjectConfig { - projectConfigShared = ProjectConfigShared { - projectConfigHcFlavor, - projectConfigHcPath, - projectConfigHcPkg - }, - projectConfigLocalPackages = PackageConfig { - packageConfigProgramPaths, - packageConfigProgramArgs, - packageConfigProgramPathExtra - } - } = do - progsearchpath <- liftIO $ getSystemSearchPath - rerunIfChanged verbosity fileMonitorCompiler - (hcFlavor, hcPath, hcPkg, progsearchpath, - packageConfigProgramPaths, - packageConfigProgramArgs, - packageConfigProgramPathExtra) $ do - - liftIO $ info verbosity "Compiler settings changed, reconfiguring..." - result@(_, _, progdb') <- liftIO $ - Cabal.configCompilerEx - hcFlavor hcPath hcPkg - progdb verbosity - - -- Note that we added the user-supplied program locations and args - -- for /all/ programs, not just those for the compiler prog and - -- compiler-related utils. In principle we don't know which programs - -- the compiler will configure (and it does vary between compilers). - -- We do know however that the compiler will only configure the - -- programs it cares about, and those are the ones we monitor here. - monitorFiles (programsMonitorFiles progdb') - - return result - where - hcFlavor = flagToMaybe projectConfigHcFlavor - hcPath = flagToMaybe projectConfigHcPath - hcPkg = flagToMaybe projectConfigHcPkg - progdb = - userSpecifyPaths (Map.toList (getMapLast packageConfigProgramPaths)) - . userSpecifyArgss (Map.toList (getMapMappend packageConfigProgramArgs)) - . modifyProgramSearchPath - (++ [ ProgramSearchPathDir dir - | dir <- fromNubList packageConfigProgramPathExtra ]) - $ defaultProgramDb --} -- Configuring other programs. -- diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/Foo.hs b/cabal-testsuite/PackageTests/ConditionalAndImport/Foo.hs new file mode 100644 index 00000000000..efbf93bbde8 --- /dev/null +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/Foo.hs @@ -0,0 +1 @@ +module Foo where diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out new file mode 100644 index 00000000000..b6a1f792e51 --- /dev/null +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out @@ -0,0 +1,12 @@ +# cabal v2-update +Downloading the latest package list from test-local-repo +# cabal v2-run +Resolving dependencies... +Build profile: -w ghc- -O1 +In order, the following will be built: + - some-exe-0.0.1.0 (exe:some-exe) (requires build) +Configuring some-exe-0.0.1.0... +Preprocessing executable 'some-exe' for some-exe-0.0.1.0.. +Building executable 'some-exe' for some-exe-0.0.1.0.. +Installing executable some-exe in +Warning: The directory /cabal.dist/home/.cabal/store/ghc-/incoming/new-/cabal.dist/home/.cabal/store/ghc-/-/bin is not in the system search path. diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.project b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.project new file mode 100644 index 00000000000..8518c69bed4 --- /dev/null +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.project @@ -0,0 +1,3 @@ +packages: . + +import extra.project \ No newline at end of file diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs new file mode 100644 index 00000000000..06ba0db652c --- /dev/null +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs @@ -0,0 +1,4 @@ +import Test.Cabal.Prelude +main = cabalTest $ + withRepo "repo" $ do + cabal "v2-run" [ "some-exe" ] diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/extra.project b/cabal-testsuite/PackageTests/ConditionalAndImport/extra.project new file mode 100644 index 00000000000..f83687d44f9 --- /dev/null +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/extra.project @@ -0,0 +1,8 @@ +if(os(NoSuchOs) || False) + extra-packages: no-such-package +elif(os(Whoops)) + extra-packages: no-can-do +elif(True) + extra-packages: some-exe +else + extra-packages: nope \ No newline at end of file diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/my.cabal b/cabal-testsuite/PackageTests/ConditionalAndImport/my.cabal new file mode 100644 index 00000000000..b1b36c1e620 --- /dev/null +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/my.cabal @@ -0,0 +1,9 @@ +name: my +version: 0.1 +license: BSD3 +cabal-version: >= 1.2 +build-type: Simple + +library + exposed-modules: Foo + build-depends: base diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/repo/some-exe-0.0.1.0/Main.hs b/cabal-testsuite/PackageTests/ConditionalAndImport/repo/some-exe-0.0.1.0/Main.hs new file mode 100644 index 00000000000..33581fa8421 --- /dev/null +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/repo/some-exe-0.0.1.0/Main.hs @@ -0,0 +1,4 @@ +module Main where + +main :: IO () +main = putStrLn "hello world" diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/repo/some-exe-0.0.1.0/some-exe.cabal b/cabal-testsuite/PackageTests/ConditionalAndImport/repo/some-exe-0.0.1.0/some-exe.cabal new file mode 100644 index 00000000000..3a2e620d96e --- /dev/null +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/repo/some-exe-0.0.1.0/some-exe.cabal @@ -0,0 +1,9 @@ +name: some-exe +version: 0.0.1.0 +license: BSD3 +cabal-version: >= 1.2 +build-type: Simple + +Executable some-exe + main-is: Main.hs + build-depends: base diff --git a/doc/cabal-project.rst b/doc/cabal-project.rst index 23083f9d0a9..5c911ca2f90 100644 --- a/doc/cabal-project.rst +++ b/doc/cabal-project.rst @@ -33,6 +33,31 @@ options): 4. ``cabal.project.local`` (the output of ``cabal v2-configure``) +Conditionals and imports +------------------------ + +As of ``cabal-install`` version 3.8, cabal supports conditional logic +and imports in ``cabal.project`` files. Cabal supports +:ref:`conditions` that case on operating system, architecture, and +compiler x(i.e. there is no support for a notion of custom flags in +project files). Imports may specify local filepaths or remote urls, +and may reference either cabal.project files or v1-style cabal.config +freeze files. As a usage example: + +:: + if(os(darwin)) + optimization: False + elif(os(freebsd)) + packages: freebsd/*.cabal + else + optimization: True + + import https://some.remote.source/subdir/cabal.config + + import relativepath/extra-project.project + + import /absolutepath/some-project.project + Specifying the local packages ----------------------------- From e8ae8e048b7c530128d78431b25a3e36a479ef16 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Fri, 7 Jan 2022 13:12:40 -0500 Subject: [PATCH 11/22] fix docs --- doc/cabal-package.rst | 2 ++ doc/cabal-project.rst | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/cabal-package.rst b/doc/cabal-package.rst index 935dd1c8377..30e6b629be4 100644 --- a/doc/cabal-package.rst +++ b/doc/cabal-package.rst @@ -2559,6 +2559,8 @@ Since Cabal 2.2 conditional blocks support ``elif`` construct. else property-descriptions-or-conditionals +.. _conditions: + Conditions """""""""" diff --git a/doc/cabal-project.rst b/doc/cabal-project.rst index 5c911ca2f90..36aeed6e4b9 100644 --- a/doc/cabal-project.rst +++ b/doc/cabal-project.rst @@ -37,14 +37,15 @@ Conditionals and imports ------------------------ As of ``cabal-install`` version 3.8, cabal supports conditional logic -and imports in ``cabal.project`` files. Cabal supports -:ref:`conditions` that case on operating system, architecture, and -compiler x(i.e. there is no support for a notion of custom flags in +and imports in ``cabal.project`` files. :ref:`conditions` in cabal +projects not be nested, and may case on operating system, architecture, and +compiler (i.e. there is no support for a notion of custom flags in project files). Imports may specify local filepaths or remote urls, and may reference either cabal.project files or v1-style cabal.config freeze files. As a usage example: :: + if(os(darwin)) optimization: False elif(os(freebsd)) From 189262c58edc9cd6efeb5b905abf614486cc4f0e Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Tue, 11 Jan 2022 13:11:59 -0500 Subject: [PATCH 12/22] use legacyReadFields parser --- .../src/Distribution/Client/ParseUtils.hs | 1 - .../Client/ProjectConfig/Legacy.hs | 170 ++++++++---------- .../ConditionalAndImport/cabal.project | 2 +- .../ConditionalAndImport/extra.project | 2 +- doc/cabal-project.rst | 8 +- 5 files changed, 81 insertions(+), 102 deletions(-) diff --git a/cabal-install/src/Distribution/Client/ParseUtils.hs b/cabal-install/src/Distribution/Client/ParseUtils.hs index ef94a167712..5a3319f1e31 100644 --- a/cabal-install/src/Distribution/Client/ParseUtils.hs +++ b/cabal-install/src/Distribution/Client/ParseUtils.hs @@ -365,4 +365,3 @@ parseConfig fieldDescrs sectionDescrs fgSectionDescrs empty str = -- showConfig :: [FieldDescr a] -> [SectionDescr a] -> [FGSectionDescr FG.PrettyFieldGrammar a] -> a -> Disp.Doc showConfig = ppFieldsAndSections - diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index 4454a58615e..69a6a9a2e7b 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -52,7 +52,7 @@ import Distribution.FieldGrammar import Distribution.Package import Distribution.Types.SourceRepo (RepoType) import Distribution.Types.CondTree - ( CondTree (..), CondBranch (..), condIfThen, condIfThenElse, mapTreeConds, traverseCondTreeC ) + ( CondTree (..), CondBranch (..), mapTreeConds, traverseCondTreeC ) import Distribution.PackageDescription ( dispFlagAssignment, Condition (..), ConfVar (..), FlagAssignment ) import Distribution.PackageDescription.Configuration (simplifyWithSysParams) @@ -90,8 +90,6 @@ import Distribution.Deprecated.ReadP import qualified Text.PrettyPrint as Disp import Text.PrettyPrint ( Doc, ($+$) ) -import qualified Text.Parsec.Error as P -import qualified Text.Parsec.Pos as P import qualified Distribution.Deprecated.ParseUtils as ParseUtils import Distribution.Deprecated.ParseUtils ( ParseResult(..), PError(..), syntaxError, PWarning(..) @@ -155,98 +153,78 @@ instantiateProjectConfigSkeleton os arch impl _flags skel = go $ mapTreeConds (f projectSkeletonImports :: ProjectConfigSkeleton -> [ProjectConfigImport] projectSkeletonImports = view traverseCondTreeC --- NOTE a nice refactor would be to use readFields directly to get a tree structure. parseProjectSkeleton :: FilePath -> HttpTransport -> Verbosity -> FilePath -> BS.ByteString -> IO (ParseResult ProjectConfigSkeleton) -parseProjectSkeleton cacheDir httpTransport verbosity source bs = (>>= sanityWalkPCS False) . runInnerParsers <$> linesToNode (BS.lines bs) - where - -- converts lines to a full tree node, recursively looping "go" to pull out conditional and import structure, then packing the whole thing up - linesToNode :: [BS.ByteString] -> IO (CondTree BS.ByteString [ProjectConfigImport] [BS.ByteString]) - linesToNode xs = (\(branches, imps, ls) -> CondNode ls imps branches) . mconcat <$> go xs - - -- given a list of lines, pulls out the conditional and import structure - go :: [BS.ByteString] -> IO [([CondBranch BS.ByteString [ProjectConfigImport] [BS.ByteString]], [ProjectConfigImport], [BS.ByteString])] - go (l:ls) - | (BS.pack "if(") `BS.isPrefixOf` l = - let (clause, rest) = splitWhileIndented ls - - -- unpacks the results of loop into nested if else clauses - constructNestedConds topCond topClause [] [] = - do c1 <- linesToNode topClause - pure $ condIfThen (Var topCond) c1 - constructNestedConds topCond topClause ((elifCond, elifClause):elifs) elseClause = - do c1 <- linesToNode topClause - condIfThenElse (Var topCond) c1 . CondNode [] [] . (:[]) <$> constructNestedConds elifCond elifClause elifs elseClause - constructNestedConds topCond topClause [] elseClause = - do c1 <- linesToNode topClause - c2 <- linesToNode elseClause - pure $ condIfThenElse (Var topCond) c1 c2 - - -- parse out the full list of if/else clauses - loop acc rss = - case rss of - (r:rs) - | BS.pack "elif" `BS.isPrefixOf` r -> - let (elseClause, lastRest) = splitWhileIndented rs - in loop ((r, elseClause):acc) lastRest - | BS.pack "else" `BS.isPrefixOf` r -> - let (elseClause, lastRest) = splitWhileIndented rs - in constructNestedConds l clause (reverse acc) elseClause - >>= (\c -> ((([c],[],[]) :) <$> go lastRest)) - _ -> constructNestedConds l clause (reverse acc) [] - >>= (\c -> ((([c],[],[]) :) <$> go rss)) - in loop [] rest - - | Just imp <- parseImport l = do x <- go . BS.lines =<< fetchImportConfig imp - ((([], [imp], []) : x) ++) <$> go ls - - | otherwise = (([], [], [l]) :) <$> go ls - - go [] = pure [] - - splitWhileIndented = span ((BS.pack " ") `BS.isPrefixOf`) - - parseImport l | (BS.pack "import ") `BS.isPrefixOf` l = Just . BS.unpack $ BS.drop (length "import ") l - | otherwise = Nothing - - runInnerParsers :: CondTree BS.ByteString [ProjectConfigImport] [BS.ByteString] -> ParseResult ProjectConfigSkeleton - runInnerParsers = (runConditionParsers =<<) . traverse (fmap (addProvenance . convertLegacyProjectConfig) . parseLegacyProjectConfig source . BS.unlines) - - runConditionParsers :: (CondTree BS.ByteString [ProjectConfigImport] ProjectConfig) -> ParseResult ProjectConfigSkeleton - runConditionParsers (CondNode d c x) = CondNode d c <$> mapM runBranchConditionParser x - - runBranchConditionParser :: CondBranch BS.ByteString [ProjectConfigImport] ProjectConfig -> ParseResult (CondBranch ConfVar [ProjectConfigImport] ProjectConfig) - runBranchConditionParser (CondBranch (Var b) t f) = do - c <- adaptParseError $ parseConditionConfVarFromClause b -- nb this loses the source line location, can't win 'em all, sigh. A full refactor of parsers is the "right" way to fix this. We could also pack bytestrings for conditionals with line locations - CondBranch c <$> runConditionParsers t <*> traverse runConditionParsers f - runBranchConditionParser (CondBranch x _ _) = error $ "internal cabal invariant error in parsing branch conditions: " ++ show x - - adaptParseError :: Either P.ParseError a -> ParseResult a - adaptParseError (Right x) = pure x - adaptParseError (Left e) = parseFail $ ParseUtils.FromString (show e) (Just . P.sourceLine $ P.errorPos e) - - addProvenance x = x {projectConfigProvenance = Set.singleton (Explicit source)} - - modifiesCompiler :: ProjectConfig -> Bool - modifiesCompiler pc = isSet projectConfigHcFlavor || isSet projectConfigHcPath || isSet projectConfigHcPkg - where - isSet f = f (projectConfigShared pc) /= NoFlag - - sanityWalkPCS :: Bool -> ProjectConfigSkeleton -> ParseResult ProjectConfigSkeleton - sanityWalkPCS underConditional t@(CondNode d _c comps) +parseProjectSkeleton cacheDir httpTransport verbosity source bs = (sanityWalkPCS False =<<) <$> liftPR (go []) (ParseUtils.readFields bs) + where + go :: [ParseUtils.Field] -> [ParseUtils.Field] -> IO (ParseResult ProjectConfigSkeleton) + go acc (x:xs) = case x of + (ParseUtils.F _l "import" importLoc) -> do + let fs = fmap singletonProjectConfigSkeleton $ fieldsToConfig (reverse acc) + res <- parseProjectSkeleton cacheDir httpTransport verbosity importLoc =<< fetchImportConfig importLoc + rest <- go [] xs + pure . fmap mconcat . sequence $ [fs, res, rest] + (ParseUtils.Section l "if" p xs') -> do + subpcs <- go [] xs' + let fs = fmap singletonProjectConfigSkeleton $ fieldsToConfig (reverse acc) + (elseClauses, rest) <- parseElseClauses xs + let condNode = (\c pcs e -> CondNode mempty mempty [CondBranch c pcs e]) <$> + -- we rewrap as as a section so the readFields lexer of the conditional parser doesn't get confused + adaptParseError l (parseConditionConfVarFromClause . BS.pack $ "if(" <> p <> ")") <*> + subpcs <*> + elseClauses + pure . fmap mconcat . sequence $ [fs, condNode, rest] + _ -> go (x:acc) xs + go acc [] = pure . fmap singletonProjectConfigSkeleton . fieldsToConfig $ reverse acc + + parseElseClauses :: [ParseUtils.Field] -> IO (ParseResult (Maybe ProjectConfigSkeleton), ParseResult ProjectConfigSkeleton) + parseElseClauses x = case x of + (ParseUtils.Section _l "else" _p xs':xs) -> do + subpcs <- go [] xs' + rest <- go [] xs + pure (Just <$> subpcs, rest) + (ParseUtils.Section l "elif" p xs':xs) -> do + subpcs <- go [] xs' + (elseClauses, rest) <- parseElseClauses xs + let condNode = (\c pcs e -> CondNode mempty mempty [CondBranch c pcs e]) <$> + adaptParseError l (parseConditionConfVarFromClause . BS.pack $ "else("<> p <> ")") <*> + subpcs <*> + elseClauses + pure (Just <$> condNode, rest) + _ -> (\r -> (pure Nothing,r)) <$> go [] x + + fieldsToConfig xs = fmap (addProvenance . convertLegacyProjectConfig) $ parseLegacyProjectConfigFields source xs + addProvenance x = x {projectConfigProvenance = Set.singleton (Explicit source)} + + adaptParseError _ (Right x) = pure x + adaptParseError l (Left e) = parseFail $ ParseUtils.FromString (show e) (Just l) + + liftPR :: (a -> IO (ParseResult b)) -> ParseResult a -> IO (ParseResult b) + liftPR f (ParseOk ws x) = addWarnings <$> f x + where addWarnings (ParseOk ws' x') = ParseOk (ws' ++ ws) x' + addWarnings x' = x' + liftPR _ (ParseFailed e) = pure $ ParseFailed e + + fetchImportConfig :: ProjectConfigImport -> IO BS.ByteString + fetchImportConfig pci = case parseURI pci of + Just uri -> do + let fp = cacheDir map (\x -> if isPathSeparator x then 'X' else x) (show uri) -- TODO can we do better? + createDirectoryIfMissing True cacheDir + _ <- downloadURI httpTransport verbosity uri fp + BS.readFile fp + Nothing -> BS.readFile pci + + modifiesCompiler :: ProjectConfig -> Bool + modifiesCompiler pc = isSet projectConfigHcFlavor || isSet projectConfigHcPath || isSet projectConfigHcPkg + where + isSet f = f (projectConfigShared pc) /= NoFlag + + sanityWalkPCS :: Bool -> ProjectConfigSkeleton -> ParseResult ProjectConfigSkeleton + sanityWalkPCS underConditional t@(CondNode d _c comps) | underConditional && modifiesCompiler d = parseFail $ ParseUtils.FromString "Cannot set compiler in a conditional clause of a cabal project file" Nothing | otherwise = mapM_ sanityWalkBranch comps >> pure t - sanityWalkBranch:: CondBranch ConfVar [ProjectConfigImport] ProjectConfig -> ParseResult () - sanityWalkBranch (CondBranch _c t f) = traverse (sanityWalkPCS True) f >> sanityWalkPCS True t >> pure () - - fetchImportConfig :: ProjectConfigImport -> IO BS.ByteString - fetchImportConfig pci = case parseURI pci of - Just uri -> do - let fp = cacheDir map (\x -> if isPathSeparator x then 'X' else x) (show uri) -- TODO can we do better? - createDirectoryIfMissing True cacheDir - _ <- downloadURI httpTransport verbosity uri fp - BS.readFile fp - Nothing -> BS.readFile pci + sanityWalkBranch:: CondBranch ConfVar [ProjectConfigImport] ProjectConfig -> ParseResult () + sanityWalkBranch (CondBranch _c t f) = traverse (sanityWalkPCS True) f >> sanityWalkPCS True t >> pure () ------------------------------------------------------------------ -- Representing the project config file in terms of legacy types @@ -999,16 +977,18 @@ convertToLegacyPerPackageConfig PackageConfig {..} = -- Parsing and showing the project config file -- - -parseLegacyProjectConfig :: FilePath -> BS.ByteString -> ParseResult LegacyProjectConfig -parseLegacyProjectConfig source = - parseConfig (legacyProjectConfigFieldDescrs constraintSrc) +parseLegacyProjectConfigFields :: FilePath -> [ParseUtils.Field] -> ParseResult LegacyProjectConfig +parseLegacyProjectConfigFields source = + parseFieldsAndSections (legacyProjectConfigFieldDescrs constraintSrc) legacyPackageConfigSectionDescrs legacyPackageConfigFGSectionDescrs mempty where constraintSrc = ConstraintSourceProjectConfig source +parseLegacyProjectConfig :: FilePath -> BS.ByteString -> ParseResult LegacyProjectConfig +parseLegacyProjectConfig source bs = parseLegacyProjectConfigFields source =<< ParseUtils.readFields bs + showLegacyProjectConfig :: LegacyProjectConfig -> String showLegacyProjectConfig config = Disp.render $ diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.project b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.project index 8518c69bed4..0aeed82370c 100644 --- a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.project +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.project @@ -1,3 +1,3 @@ packages: . -import extra.project \ No newline at end of file +import: extra.project diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/extra.project b/cabal-testsuite/PackageTests/ConditionalAndImport/extra.project index f83687d44f9..52a1f847fbb 100644 --- a/cabal-testsuite/PackageTests/ConditionalAndImport/extra.project +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/extra.project @@ -5,4 +5,4 @@ elif(os(Whoops)) elif(True) extra-packages: some-exe else - extra-packages: nope \ No newline at end of file + extra-packages: nope diff --git a/doc/cabal-project.rst b/doc/cabal-project.rst index 36aeed6e4b9..3049f20fed5 100644 --- a/doc/cabal-project.rst +++ b/doc/cabal-project.rst @@ -38,7 +38,7 @@ Conditionals and imports As of ``cabal-install`` version 3.8, cabal supports conditional logic and imports in ``cabal.project`` files. :ref:`conditions` in cabal -projects not be nested, and may case on operating system, architecture, and +may case on operating system, architecture, and compiler (i.e. there is no support for a notion of custom flags in project files). Imports may specify local filepaths or remote urls, and may reference either cabal.project files or v1-style cabal.config @@ -53,11 +53,11 @@ freeze files. As a usage example: else optimization: True - import https://some.remote.source/subdir/cabal.config + import: https://some.remote.source/subdir/cabal.config - import relativepath/extra-project.project + import: relativepath/extra-project.project - import /absolutepath/some-project.project + import: /absolutepath/some-project.project Specifying the local packages ----------------------------- From 1233acf8e4ab2ead76c5796de5f11fa0a5f9bde8 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Tue, 1 Feb 2022 17:01:17 -0500 Subject: [PATCH 13/22] changelog --- changelog.d/pr-7783 | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 changelog.d/pr-7783 diff --git a/changelog.d/pr-7783 b/changelog.d/pr-7783 new file mode 100644 index 00000000000..f15e61fdef2 --- /dev/null +++ b/changelog.d/pr-7783 @@ -0,0 +1,10 @@ +synopsis: Conditionals and imports in cabal.project files +packages: cabal-install +prs: #7783 +issues: #7556 + +description: { + +Cabal.project files now allow conditional logic on compiler version, arch, etc. as well as imports of other local or remote project of freeze files (both old and new style). + +} From 782892f4986f530fbe7deb447290ed1a5017468a Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Wed, 2 Feb 2022 11:30:34 -0500 Subject: [PATCH 14/22] cyclical import detection --- .../src/Distribution/Client/CmdOutdated.hs | 2 -- .../src/Distribution/Client/ProjectConfig.hs | 2 +- .../Distribution/Client/ProjectConfig/Legacy.hs | 17 ++++++++++------- .../PackageTests/ConditionalAndImport/cabal.out | 3 +++ .../ConditionalAndImport/cabal.test.hs | 1 + 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/cabal-install/src/Distribution/Client/CmdOutdated.hs b/cabal-install/src/Distribution/Client/CmdOutdated.hs index 7753078c6d2..b38cfc02f8b 100644 --- a/cabal-install/src/Distribution/Client/CmdOutdated.hs +++ b/cabal-install/src/Distribution/Client/CmdOutdated.hs @@ -87,12 +87,10 @@ import Distribution.Client.HttpUtils import Distribution.Utils.NubList ( fromNubList ) - import qualified Data.Set as S import System.Directory ( getCurrentDirectory, doesFileExist ) - ------------------------------------------------------------------------------- -- Command ------------------------------------------------------------------------------- diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index c1811902c3d..223a3160146 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -581,7 +581,7 @@ readProjectFileSkeleton verbosity httpTransport DistDirLayout{distProjectFile, d readExtensionFile = reportParseResult verbosity extensionDescription extensionFile - =<< parseProjectSkeleton distDownloadSrcDirectory httpTransport verbosity extensionFile + =<< parseProjectSkeleton distDownloadSrcDirectory httpTransport verbosity [] extensionFile =<< BS.readFile extensionFile -- | Render the 'ProjectConfig' format. diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index c5b618193a6..bde47a19532 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -154,16 +154,19 @@ instantiateProjectConfigSkeleton os arch impl _flags skel = go $ mapTreeConds (f projectSkeletonImports :: ProjectConfigSkeleton -> [ProjectConfigImport] projectSkeletonImports = view traverseCondTreeC -parseProjectSkeleton :: FilePath -> HttpTransport -> Verbosity -> FilePath -> BS.ByteString -> IO (ParseResult ProjectConfigSkeleton) -parseProjectSkeleton cacheDir httpTransport verbosity source bs = (sanityWalkPCS False =<<) <$> liftPR (go []) (ParseUtils.readFields bs) +parseProjectSkeleton :: FilePath -> HttpTransport -> Verbosity -> [ProjectConfigImport] -> FilePath -> BS.ByteString -> IO (ParseResult ProjectConfigSkeleton) +parseProjectSkeleton cacheDir httpTransport verbosity seenImports source bs = (sanityWalkPCS False =<<) <$> liftPR (go []) (ParseUtils.readFields bs) where go :: [ParseUtils.Field] -> [ParseUtils.Field] -> IO (ParseResult ProjectConfigSkeleton) go acc (x:xs) = case x of - (ParseUtils.F _l "import" importLoc) -> do - let fs = fmap singletonProjectConfigSkeleton $ fieldsToConfig (reverse acc) - res <- parseProjectSkeleton cacheDir httpTransport verbosity importLoc =<< fetchImportConfig importLoc - rest <- go [] xs - pure . fmap mconcat . sequence $ [fs, res, rest] + (ParseUtils.F l "import" importLoc) -> + if importLoc `elem` seenImports + then pure . parseFail $ ParseUtils.FromString ("cyclical import of " ++ importLoc) (Just l) + else do + let fs = fmap (\x -> CondNode x [importLoc] mempty) $ fieldsToConfig (reverse acc) + res <- parseProjectSkeleton cacheDir httpTransport verbosity (importLoc : seenImports) importLoc =<< fetchImportConfig importLoc + rest <- go [] xs + pure . fmap mconcat . sequence $ [fs, res, rest] (ParseUtils.Section l "if" p xs') -> do subpcs <- go [] xs' let fs = fmap singletonProjectConfigSkeleton $ fieldsToConfig (reverse acc) diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out index b6a1f792e51..acd94e8947b 100644 --- a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out @@ -10,3 +10,6 @@ Preprocessing executable 'some-exe' for some-exe-0.0.1.0.. Building executable 'some-exe' for some-exe-0.0.1.0.. Installing executable some-exe in Warning: The directory /cabal.dist/home/.cabal/store/ghc-/incoming/new-/cabal.dist/home/.cabal/store/ghc-/-/bin is not in the system search path. +# cabal v2-build +Error: cabal: Error parsing project file /cabal-cyclical.project:3: +cyclical import of cabal-cyclical.project diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs index 06ba0db652c..f1f125207e2 100644 --- a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs @@ -2,3 +2,4 @@ import Test.Cabal.Prelude main = cabalTest $ withRepo "repo" $ do cabal "v2-run" [ "some-exe" ] + fails $ cabal "v2-build" [ "--project=cabal-cyclical.project" ] From 0fcc0b58dd959f51c9b705bc3bb89825c2e007b4 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Wed, 2 Feb 2022 12:18:18 -0500 Subject: [PATCH 15/22] fix shadowing --- cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index bde47a19532..fc31eb14151 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -163,7 +163,7 @@ parseProjectSkeleton cacheDir httpTransport verbosity seenImports source bs = (s if importLoc `elem` seenImports then pure . parseFail $ ParseUtils.FromString ("cyclical import of " ++ importLoc) (Just l) else do - let fs = fmap (\x -> CondNode x [importLoc] mempty) $ fieldsToConfig (reverse acc) + let fs = fmap (\z -> CondNode z [importLoc] mempty) $ fieldsToConfig (reverse acc) res <- parseProjectSkeleton cacheDir httpTransport verbosity (importLoc : seenImports) importLoc =<< fetchImportConfig importLoc rest <- go [] xs pure . fmap mconcat . sequence $ [fs, res, rest] From 9867fbcee3b638124edf412dc3743924b8666857 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Wed, 2 Feb 2022 15:27:10 -0500 Subject: [PATCH 16/22] add missing file --- .../PackageTests/ConditionalAndImport/cabal-cyclical.project | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 cabal-testsuite/PackageTests/ConditionalAndImport/cabal-cyclical.project diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal-cyclical.project b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal-cyclical.project new file mode 100644 index 00000000000..db226311abc --- /dev/null +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal-cyclical.project @@ -0,0 +1,3 @@ +packages: . + +import: cabal-cyclical.project From 5d59b1814e2cdb3d140a3bb95fd4492c8d089fe2 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Fri, 25 Mar 2022 16:30:19 -0400 Subject: [PATCH 17/22] finish merge --- .../src/Distribution/Client/ProjectConfig.hs | 1 - .../Distribution/Client/ProjectPlanning.hs | 44 +------------------ .../src/Distribution/Client/ScriptUtils.hs | 42 +++++++++++++++--- 3 files changed, 37 insertions(+), 50 deletions(-) diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index 4292e1608bd..bb6f07d3b9c 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -29,7 +29,6 @@ module Distribution.Client.ProjectConfig ( readGlobalConfig, readProjectLocalExtraConfig, readProjectLocalFreezeConfig, - parseProjectConfig, reportParseResult, showProjectConfig, withProjectOrGlobalConfig, diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index e5589b7e3c0..9ca461b9e6f 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -40,6 +40,7 @@ module Distribution.Client.ProjectPlanning ( -- * Utils required for building pkgHasEphemeralBuildTargets, elabBuildTargetWholeComponents, + configureCompiler, -- * Setup.hs CLI flags for building setupHsScriptOptions, @@ -532,48 +533,7 @@ rebuildInstallPlan verbosity -- phaseConfigureCompiler :: ProjectConfig -> Rebuild (Compiler, Platform, ProgramDb) - phaseConfigureCompiler ProjectConfig { - projectConfigShared = ProjectConfigShared { - projectConfigHcFlavor, - projectConfigHcPath, - projectConfigHcPkg - }, - projectConfigLocalPackages = PackageConfig { - packageConfigProgramPaths, - packageConfigProgramPathExtra - } - } = do - progsearchpath <- liftIO $ getSystemSearchPath - rerunIfChanged verbosity fileMonitorCompiler - (hcFlavor, hcPath, hcPkg, progsearchpath, - packageConfigProgramPaths, - packageConfigProgramPathExtra) $ do - - liftIO $ info verbosity "Compiler settings changed, reconfiguring..." - result@(_, _, progdb') <- liftIO $ - Cabal.configCompilerEx - hcFlavor hcPath hcPkg - progdb verbosity - - -- Note that we added the user-supplied program locations and args - -- for /all/ programs, not just those for the compiler prog and - -- compiler-related utils. In principle we don't know which programs - -- the compiler will configure (and it does vary between compilers). - -- We do know however that the compiler will only configure the - -- programs it cares about, and those are the ones we monitor here. - monitorFiles (programsMonitorFiles progdb') - - return result - where - hcFlavor = flagToMaybe projectConfigHcFlavor - hcPath = flagToMaybe projectConfigHcPath - hcPkg = flagToMaybe projectConfigHcPkg - progdb = - userSpecifyPaths (Map.toList (getMapLast packageConfigProgramPaths)) - . modifyProgramSearchPath - (++ [ ProgramSearchPathDir dir - | dir <- fromNubList packageConfigProgramPathExtra ]) - $ defaultProgramDb + phaseConfigureCompiler = configureCompiler verbosity distDirLayout -- Configuring other programs. -- diff --git a/cabal-install/src/Distribution/Client/ScriptUtils.hs b/cabal-install/src/Distribution/Client/ScriptUtils.hs index 9f012380256..c83b0c07301 100644 --- a/cabal-install/src/Distribution/Client/ScriptUtils.hs +++ b/cabal-install/src/Distribution/Client/ScriptUtils.hs @@ -27,13 +27,21 @@ import Distribution.Client.DistDirLayout ( DistDirLayout(..) ) import Distribution.Client.HashValue ( hashValue, showHashValue ) +import Distribution.Client.HttpUtils + ( HttpTransport, configureTransport ) import Distribution.Client.NixStyleOptions ( NixStyleFlags (..) ) import Distribution.Client.ProjectConfig ( ProjectConfig(..), ProjectConfigShared(..) - , parseProjectConfig, reportParseResult, withProjectOrGlobalConfig ) + , reportParseResult, withProjectOrGlobalConfig + , projectConfigHttpTransport ) +import Distribution.Client.ProjectConfig.Legacy + ( ProjectConfigSkeleton + , parseProjectSkeleton, instantiateProjectConfigSkeleton ) import Distribution.Client.ProjectFlags ( flagIgnoreProject ) +import Distribution.Client.RebuildMonad + ( runRebuild ) import Distribution.Client.Setup ( ConfigFlags(..), GlobalFlags(..) ) import Distribution.Client.TargetSelector @@ -44,6 +52,8 @@ import Distribution.FieldGrammar ( parseFieldGrammar, takeFields ) import Distribution.Fields ( ParseResult, parseFatalFailure, readFields ) +import Distribution.PackageDescription + ( ignoreConditions ) import Distribution.PackageDescription.FieldGrammar ( executableFieldGrammar ) import Distribution.PackageDescription.PrettyPrint @@ -51,16 +61,20 @@ import Distribution.PackageDescription.PrettyPrint import Distribution.Parsec ( Position(..) ) import Distribution.Simple.Flag - ( fromFlagOrDefault ) + ( fromFlagOrDefault, flagToMaybe ) import Distribution.Simple.PackageDescription ( parseString ) import Distribution.Simple.Setup ( Flag(..) ) +import Distribution.Simple.Compiler + ( compilerInfo ) import Distribution.Simple.Utils ( createDirectoryIfMissingVerbose, createTempDirectory, die', handleDoesNotExist, readUTF8File, warn, writeUTF8File ) import qualified Distribution.SPDX.License as SPDX import Distribution.Solver.Types.SourcePackage as SP ( SourcePackage(..) ) +import Distribution.System + ( Platform(..) ) import Distribution.Types.BuildInfo ( BuildInfo(..) ) import Distribution.Types.CondTree @@ -73,6 +87,10 @@ import Distribution.Types.PackageDescription ( PackageDescription(..), emptyPackageDescription ) import Distribution.Types.PackageName.Magic ( fakePackageCabalFileName, fakePackageId ) +import Distribution.Utils.NubList + ( fromNubList ) +import Distribution.Client.ProjectPlanning + ( configureCompiler ) import Distribution.Verbosity ( normal ) import Language.Haskell.Extension @@ -217,7 +235,17 @@ withContextAndSelectors noTargets kind flags@NixStyleFlags {..} targetStrings gl scriptContents <- BS.readFile script executable <- readExecutableBlockFromScript verbosity scriptContents - projectCfg <- readProjectBlockFromScript verbosity (takeFileName script) scriptContents + + + httpTransport <- configureTransport verbosity + (fromNubList . projectConfigProgPathExtra $ projectConfigShared cliConfig) + (flagToMaybe . projectConfigHttpTransport $ projectConfigBuildOnly cliConfig) + + projectCfgSkeleton <- readProjectBlockFromScript verbosity httpTransport (distDirLayout ctx) (takeFileName script) scriptContents + + (compiler, Platform arch os, _) <- runRebuild (distProjectRootDirectory . distDirLayout $ ctx) $ configureCompiler verbosity (distDirLayout ctx) (fst $ ignoreConditions projectCfgSkeleton) + + let projectCfg = instantiateProjectConfigSkeleton os arch (compilerInfo compiler) mempty projectCfgSkeleton :: ProjectConfig let executable' = executable & L.buildInfo . L.defaultLanguage %~ maybe (Just Haskell2010) Just ctx' = ctx & lProjectConfig %~ (<> projectCfg) @@ -312,12 +340,12 @@ readExecutableBlockFromScript verbosity str = do -- * @-}@ -- -- Return the metadata. -readProjectBlockFromScript :: Verbosity -> String -> BS.ByteString -> IO ProjectConfig -readProjectBlockFromScript verbosity scriptName str = do +readProjectBlockFromScript :: Verbosity -> HttpTransport -> DistDirLayout -> String -> BS.ByteString -> IO ProjectConfigSkeleton +readProjectBlockFromScript verbosity httpTransport DistDirLayout{distDownloadSrcDirectory} scriptName str = do case extractScriptBlock "project" str of Left _ -> return mempty - Right x -> reportParseResult verbosity "script" scriptName - $ parseProjectConfig scriptName x + Right x -> reportParseResult verbosity "script" scriptName + =<< parseProjectSkeleton distDownloadSrcDirectory httpTransport verbosity [] scriptName x -- | Extract the first encountered script metadata block started end -- terminated by the tokens From f812afbc54287c91608f6a105e1369af5cfffb36 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Sat, 26 Mar 2022 12:35:01 -0400 Subject: [PATCH 18/22] fix outstanding merge issue --- cabal-install/src/Distribution/Client/ProjectPlanning.hs | 3 --- 1 file changed, 3 deletions(-) diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 9ca461b9e6f..24e39695eca 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -405,7 +405,6 @@ configureCompiler verbosity }, projectConfigLocalPackages = PackageConfig { packageConfigProgramPaths, - packageConfigProgramArgs, packageConfigProgramPathExtra } } = do @@ -415,7 +414,6 @@ configureCompiler verbosity rerunIfChanged verbosity fileMonitorCompiler (hcFlavor, hcPath, hcPkg, progsearchpath, packageConfigProgramPaths, - packageConfigProgramArgs, packageConfigProgramPathExtra) $ do liftIO $ info verbosity "Compiler settings changed, reconfiguring..." @@ -439,7 +437,6 @@ configureCompiler verbosity hcPkg = flagToMaybe projectConfigHcPkg progdb = userSpecifyPaths (Map.toList (getMapLast packageConfigProgramPaths)) - . userSpecifyArgss (Map.toList (getMapMappend packageConfigProgramArgs)) . modifyProgramSearchPath (++ [ ProgramSearchPathDir dir | dir <- fromNubList packageConfigProgramPathExtra ]) From f3c870473643a42b3b5cd92b91ef78227d5f3b9a Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Mon, 28 Mar 2022 18:06:23 -0400 Subject: [PATCH 19/22] use existing config available when checking for compiler for package flags --- cabal-install/src/Distribution/Client/ProjectPlanning.hs | 2 +- cabal-install/src/Distribution/Client/ScriptUtils.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cabal-install/src/Distribution/Client/ProjectPlanning.hs b/cabal-install/src/Distribution/Client/ProjectPlanning.hs index 24e39695eca..11e011543f8 100644 --- a/cabal-install/src/Distribution/Client/ProjectPlanning.hs +++ b/cabal-install/src/Distribution/Client/ProjectPlanning.hs @@ -333,7 +333,7 @@ rebuildProjectConfig verbosity liftIO $ createDirectoryIfMissingVerbose verbosity True distProjectCacheDirectory projectConfigSkeleton <- phaseReadProjectConfig -- have to create the cache directory before configuring the compiler - (compiler, Platform arch os, _) <- configureCompiler verbosity distDirLayout (fst $ PD.ignoreConditions projectConfigSkeleton) + (compiler, Platform arch os, _) <- configureCompiler verbosity distDirLayout ((fst $ PD.ignoreConditions projectConfigSkeleton) <> cliConfig) let projectConfig = instantiateProjectConfigSkeleton os arch (compilerInfo compiler) mempty projectConfigSkeleton localPackages <- phaseReadLocalPackages (projectConfig <> cliConfig) return (projectConfig, localPackages) diff --git a/cabal-install/src/Distribution/Client/ScriptUtils.hs b/cabal-install/src/Distribution/Client/ScriptUtils.hs index c83b0c07301..6555b92ef7c 100644 --- a/cabal-install/src/Distribution/Client/ScriptUtils.hs +++ b/cabal-install/src/Distribution/Client/ScriptUtils.hs @@ -243,7 +243,7 @@ withContextAndSelectors noTargets kind flags@NixStyleFlags {..} targetStrings gl projectCfgSkeleton <- readProjectBlockFromScript verbosity httpTransport (distDirLayout ctx) (takeFileName script) scriptContents - (compiler, Platform arch os, _) <- runRebuild (distProjectRootDirectory . distDirLayout $ ctx) $ configureCompiler verbosity (distDirLayout ctx) (fst $ ignoreConditions projectCfgSkeleton) + (compiler, Platform arch os, _) <- runRebuild (distProjectRootDirectory . distDirLayout $ ctx) $ configureCompiler verbosity (distDirLayout ctx) ((fst $ ignoreConditions projectCfgSkeleton) <> projectConfig ctx) let projectCfg = instantiateProjectConfigSkeleton os arch (compilerInfo compiler) mempty projectCfgSkeleton :: ProjectConfig From e70d6f982f355343effe526970b1526141fdfc71 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Wed, 30 Mar 2022 18:43:06 -0400 Subject: [PATCH 20/22] review comments --- .../src/Distribution/Client/ProjectConfig.hs | 8 +++---- .../Client/ProjectConfig/Legacy.hs | 22 +++++++++---------- .../ConditionalAndImport/cabal.out | 3 +++ .../ConditionalAndImport/cabal.test.hs | 1 + 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/cabal-install/src/Distribution/Client/ProjectConfig.hs b/cabal-install/src/Distribution/Client/ProjectConfig.hs index bb6f07d3b9c..9ca62bd63e7 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig.hs @@ -625,12 +625,12 @@ readGlobalConfig verbosity configFileFlag = do monitorFiles [monitorFileHashed configFile] return (convertLegacyGlobalConfig config) -reportParseResult :: Verbosity -> String -> FilePath -> OldParser.ParseResult a -> IO a +reportParseResult :: Verbosity -> String -> FilePath -> OldParser.ParseResult ProjectConfigSkeleton -> IO ProjectConfigSkeleton reportParseResult verbosity _filetype filename (OldParser.ParseOk warnings x) = do - unless (null warnings) $ - let msg = unlines (map (OldParser.showPWarning filename) warnings) + unless (null warnings) $ + let msg = unlines (map (OldParser.showPWarning (intercalate ", " $ filename : projectSkeletonImports x)) warnings) in warn verbosity msg - return x + return x reportParseResult verbosity filetype filename (OldParser.ParseFailed err) = let (line, msg) = OldParser.locatedErrorMsg err in die' verbosity $ "Error parsing " ++ filetype ++ " " ++ filename diff --git a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs index adeaf29cb28..2ed03cc6888 100644 --- a/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs +++ b/cabal-install/src/Distribution/Client/ProjectConfig/Legacy.hs @@ -116,7 +116,7 @@ import Network.URI (URI (..), parseURI) import Distribution.Fields.ConfVar (parseConditionConfVarFromClause) import Distribution.Client.HttpUtils -import System.FilePath ((), isPathSeparator) +import System.FilePath ((), isPathSeparator, makeValid) import System.Directory (createDirectoryIfMissing) @@ -168,15 +168,15 @@ parseProjectSkeleton cacheDir httpTransport verbosity seenImports source bs = (s rest <- go [] xs pure . fmap mconcat . sequence $ [fs, res, rest] (ParseUtils.Section l "if" p xs') -> do - subpcs <- go [] xs' - let fs = fmap singletonProjectConfigSkeleton $ fieldsToConfig (reverse acc) - (elseClauses, rest) <- parseElseClauses xs - let condNode = (\c pcs e -> CondNode mempty mempty [CondBranch c pcs e]) <$> - -- we rewrap as as a section so the readFields lexer of the conditional parser doesn't get confused - adaptParseError l (parseConditionConfVarFromClause . BS.pack $ "if(" <> p <> ")") <*> - subpcs <*> - elseClauses - pure . fmap mconcat . sequence $ [fs, condNode, rest] + subpcs <- go [] xs' + let fs = fmap singletonProjectConfigSkeleton $ fieldsToConfig (reverse acc) + (elseClauses, rest) <- parseElseClauses xs + let condNode = (\c pcs e -> CondNode mempty mempty [CondBranch c pcs e]) <$> + -- we rewrap as as a section so the readFields lexer of the conditional parser doesn't get confused + adaptParseError l (parseConditionConfVarFromClause . BS.pack $ "if(" <> p <> ")") <*> + subpcs <*> + elseClauses + pure . fmap mconcat . sequence $ [fs, condNode, rest] _ -> go (x:acc) xs go acc [] = pure . fmap singletonProjectConfigSkeleton . fieldsToConfig $ reverse acc @@ -211,7 +211,7 @@ parseProjectSkeleton cacheDir httpTransport verbosity seenImports source bs = (s fetchImportConfig :: ProjectConfigImport -> IO BS.ByteString fetchImportConfig pci = case parseURI pci of Just uri -> do - let fp = cacheDir map (\x -> if isPathSeparator x then 'X' else x) (show uri) -- TODO can we do better? + let fp = cacheDir map (\x -> if isPathSeparator x then '_' else x) (makeValid $ show uri) createDirectoryIfMissing True cacheDir _ <- downloadURI httpTransport verbosity uri fp BS.readFile fp diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out index acd94e8947b..e125635e0eb 100644 --- a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.out @@ -13,3 +13,6 @@ Warning: The directory /cabal.dist/home/.cabal/store/ghc-/incoming # cabal v2-build Error: cabal: Error parsing project file /cabal-cyclical.project:3: cyclical import of cabal-cyclical.project +# cabal v2-build +Error: cabal: Error parsing project file /cabal-bad-conditional.project: +Cannot set compiler in a conditional clause of a cabal project file diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs index f1f125207e2..0791050f66d 100644 --- a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs @@ -3,3 +3,4 @@ main = cabalTest $ withRepo "repo" $ do cabal "v2-run" [ "some-exe" ] fails $ cabal "v2-build" [ "--project=cabal-cyclical.project" ] + fails $ cabal "v2-build" [ "--project=cabal-bad-conditional.project" ] From 7525a7d3d821f0b84202bb41726c89ee0242ccd5 Mon Sep 17 00:00:00 2001 From: Gershom Bazerman Date: Wed, 30 Mar 2022 22:33:49 -0400 Subject: [PATCH 21/22] add missing test file --- .../ConditionalAndImport/cabal-bad-conditional.project | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 cabal-testsuite/PackageTests/ConditionalAndImport/cabal-bad-conditional.project diff --git a/cabal-testsuite/PackageTests/ConditionalAndImport/cabal-bad-conditional.project b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal-bad-conditional.project new file mode 100644 index 00000000000..1cd137e6de7 --- /dev/null +++ b/cabal-testsuite/PackageTests/ConditionalAndImport/cabal-bad-conditional.project @@ -0,0 +1,4 @@ +packages: . + +if(True) + compiler: ghc From 2496a7d77c0e640efe789d711d84e90f5dc640a5 Mon Sep 17 00:00:00 2001 From: gbaz Date: Thu, 31 Mar 2022 12:00:52 -0400 Subject: [PATCH 22/22] Update pr-7783 --- changelog.d/pr-7783 | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.d/pr-7783 b/changelog.d/pr-7783 index f15e61fdef2..bbf8cda6f8d 100644 --- a/changelog.d/pr-7783 +++ b/changelog.d/pr-7783 @@ -2,6 +2,7 @@ synopsis: Conditionals and imports in cabal.project files packages: cabal-install prs: #7783 issues: #7556 +significance: significant description: {