Skip to content

Commit 32259a1

Browse files
gbazmergify[bot]
andauthored
Extended project files (conditionals and imports) (#7783)
* initial parser pass * first compiling pass * get more stuff sort of working * conditional parsing actually works * error cleanup and downloads * thread through http transport * fix merge * better errors and use extended project parsing uniformly * elif support, maybe? * fix outdated cmd, add tests, docs * fix docs * use legacyReadFields parser * changelog * cyclical import detection * fix shadowing * add missing file * finish merge * fix outstanding merge issue * use existing config available when checking for compiler for package flags * review comments * add missing test file * Update pr-7783 Co-authored-by: Gershom Bazerman <[email protected]> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent ebfd8c7 commit 32259a1

File tree

26 files changed

+456
-144
lines changed

26 files changed

+456
-144
lines changed

Cabal-syntax/src/Distribution/Fields/ConfVar.hs

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
{-# LANGUAGE OverloadedStrings #-}
2-
module Distribution.Fields.ConfVar (parseConditionConfVar) where
2+
module Distribution.Fields.ConfVar (parseConditionConfVar, parseConditionConfVarFromClause) where
33

44
import Distribution.Compat.CharParsing (char, integral)
55
import Distribution.Compat.Prelude
6-
import Distribution.Fields.Field (SectionArg (..))
6+
import Distribution.Fields.Field (SectionArg (..), Field(..))
77
import Distribution.Fields.ParseResult
88
import Distribution.Parsec (Parsec (..), Position (..), runParsecParser)
99
import Distribution.Parsec.FieldLineStream (fieldLineStreamFromBS)
1010
import Distribution.Types.Condition
1111
import Distribution.Types.ConfVar (ConfVar (..))
12+
import Distribution.Fields.Parser (readFields)
1213
import Distribution.Version
1314
(anyVersion, earlierVersion, intersectVersionRanges, laterVersion, majorBoundVersion,
1415
mkVersion, noVersion, orEarlierVersion, orLaterVersion, thisVersion, unionVersionRanges,
1516
withinVersion)
1617
import Prelude ()
1718

1819
import qualified Text.Parsec as P
20+
import qualified Text.Parsec.Pos as P
1921
import qualified Text.Parsec.Error as P
22+
import qualified Data.ByteString.Char8 as B8
23+
24+
parseConditionConfVarFromClause :: B8.ByteString -> Either P.ParseError (Condition ConfVar)
25+
parseConditionConfVarFromClause x = readFields x >>= \r -> case r of
26+
(Section _ xs _ : _ ) -> P.runParser (parser <* P.eof) () "<condition>" xs
27+
_ -> Left $ P.newErrorMessage (P.Message "No fields in clause") (P.initialPos "<condition>")
2028

2129
-- | Parse @'Condition' 'ConfVar'@ from section arguments provided by parsec
2230
-- based outline parser.

Cabal-syntax/src/Distribution/PackageDescription/Configuration.hs

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ module Distribution.PackageDescription.Configuration (
3232
transformAllBuildInfos,
3333
transformAllBuildDepends,
3434
transformAllBuildDependsN,
35+
simplifyWithSysParams
3536
) where
3637

3738
import Distribution.Compat.Prelude

Cabal-syntax/src/Distribution/Types/CondTree.hs

+8-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ instance (Binary v, Binary c, Binary a) => Binary (CondTree v c a)
6868
instance (Structured v, Structured c, Structured a) => Structured (CondTree v c a)
6969
instance (NFData v, NFData c, NFData a) => NFData (CondTree v c a) where rnf = genericRnf
7070

71+
instance (Semigroup a, Semigroup c) => Semigroup (CondTree v c a) where
72+
(CondNode a c bs) <> (CondNode a' c' bs') = CondNode (a <> a') (c <> c') (bs <> bs')
73+
74+
instance (Semigroup a, Semigroup c, Monoid a, Monoid c) => Monoid (CondTree v c a) where
75+
mappend = (<>)
76+
mempty = CondNode mempty mempty mempty
77+
7178
-- | A 'CondBranch' represents a conditional branch, e.g., @if
7279
-- flag(foo)@ on some syntax @a@. It also has an optional false
7380
-- branch.
@@ -191,4 +198,4 @@ foldCondTree e u mergeInclusive mergeExclusive = goTree
191198
goTree :: CondTree v c a -> b
192199
goTree (CondNode a c ifs) = u (c, a) `mergeInclusive` foldl goBranch e ifs
193200
goBranch :: b -> CondBranch v c a -> b
194-
goBranch acc (CondBranch _ t mt) = mergeInclusive acc (maybe (goTree t) (mergeExclusive (goTree t) . goTree) mt)
201+
goBranch acc (CondBranch _ t mt) = mergeInclusive acc (maybe (goTree t) (mergeExclusive (goTree t) . goTree) mt)

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

+12-3
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,17 @@ import Distribution.Verbosity
3030
import Distribution.Simple.Command
3131
( CommandUI(..), usageAlternatives )
3232
import Distribution.Simple.Utils
33-
( wrapText, notice )
33+
( wrapText, notice, die' )
3434

3535
import Distribution.Client.DistDirLayout
3636
( DistDirLayout(..) )
3737
import Distribution.Client.RebuildMonad (runRebuild)
3838
import Distribution.Client.ProjectConfig.Types
39+
import Distribution.Client.HttpUtils
40+
import Distribution.Utils.NubList
41+
( fromNubList )
42+
import Distribution.Types.CondTree
43+
( CondTree (..) )
3944

4045
configureCommand :: CommandUI (NixStyleFlags ())
4146
configureCommand = CommandUI {
@@ -126,8 +131,12 @@ configureAction' flags@NixStyleFlags {..} _extraArgs globalFlags = do
126131
-- If the flag @configAppend@ is set to true, append and do not overwrite
127132
if exists && appends
128133
then do
129-
conf <- runRebuild (distProjectRootDirectory . distDirLayout $ baseCtx) $
130-
readProjectLocalExtraConfig v (distDirLayout baseCtx)
134+
httpTransport <- configureTransport v
135+
(fromNubList . projectConfigProgPathExtra $ projectConfigShared cliConfig)
136+
(flagToMaybe . projectConfigHttpTransport $ projectConfigBuildOnly cliConfig)
137+
(CondNode conf imps bs) <- runRebuild (distProjectRootDirectory . distDirLayout $ baseCtx) $
138+
readProjectLocalExtraConfig v httpTransport (distDirLayout baseCtx)
139+
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"
131140
return (baseCtx, conf <> cliConfig)
132141
else
133142
return (baseCtx, cliConfig)

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

+19-13
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ import Distribution.Client.DistDirLayout
2929
( defaultDistDirLayout
3030
, DistDirLayout(distProjectRootDirectory, distProjectFile) )
3131
import Distribution.Client.ProjectConfig
32-
( ProjectConfig(projectConfigShared),
33-
ProjectConfigShared(projectConfigConstraints), findProjectRoot,
34-
readProjectLocalFreezeConfig )
32+
import Distribution.Client.ProjectConfig.Legacy
33+
( instantiateProjectConfigSkeleton )
3534
import Distribution.Client.ProjectFlags
3635
( projectFlagsOptions, ProjectFlags(..), defaultProjectFlags
3736
, removeIgnoreProjectOption )
@@ -40,8 +39,6 @@ import Distribution.Client.RebuildMonad
4039
import Distribution.Client.Sandbox
4140
( loadConfigOrSandboxConfig )
4241
import Distribution.Client.Setup
43-
( withRepoContext, GlobalFlags, configCompilerAux'
44-
, ConfigExFlags(configExConstraints) )
4542
import Distribution.Client.Targets
4643
( userToPackageConstraint, UserConstraint )
4744
import Distribution.Client.Types.SourcePackageDb as SourcePackageDb
@@ -65,7 +62,7 @@ import Distribution.Simple.Setup
6562
import Distribution.Simple.Utils
6663
( die', notice, debug, tryFindPackageDesc )
6764
import Distribution.System
68-
( Platform )
65+
( Platform (..) )
6966
import Distribution.Types.ComponentRequestedSpec
7067
( ComponentRequestedSpec(..) )
7168
import Distribution.Types.Dependency
@@ -86,6 +83,9 @@ import Distribution.Simple.PackageDescription
8683
import qualified Distribution.Compat.CharParsing as P
8784
import Distribution.ReadE
8885
( parsecToReadE )
86+
import Distribution.Client.HttpUtils
87+
import Distribution.Utils.NubList
88+
( fromNubList )
8989

9090
import qualified Data.Set as S
9191
import System.Directory
@@ -220,18 +220,23 @@ outdatedAction (ProjectFlags{flagProjectFileName}, OutdatedFlags{..}) _targetStr
220220
config <- loadConfigOrSandboxConfig verbosity globalFlags
221221
let globalFlags' = savedGlobalFlags config `mappend` globalFlags
222222
configFlags = savedConfigureFlags config
223-
(comp, platform, _progdb) <- configCompilerAux' configFlags
224223
withRepoContext verbosity globalFlags' $ \repoContext -> do
225224
when (not newFreezeFile && isJust mprojectFile) $
226225
die' verbosity $
227226
"--project-file must only be used with --v2-freeze-file."
228227

229228
sourcePkgDb <- IndexUtils.getSourcePackages verbosity repoContext
229+
(comp, platform, _progdb) <- configCompilerAux' configFlags
230230
deps <- if freezeFile
231231
then depsFromFreezeFile verbosity
232232
else if newFreezeFile
233-
then depsFromNewFreezeFile verbosity mprojectFile
234-
else depsFromPkgDesc verbosity comp platform
233+
then do
234+
httpTransport <- configureTransport verbosity
235+
(fromNubList . globalProgPathExtra $ globalFlags)
236+
(flagToMaybe . globalHttpTransport $ globalFlags)
237+
depsFromNewFreezeFile verbosity httpTransport comp platform mprojectFile
238+
else do
239+
depsFromPkgDesc verbosity comp platform
235240
debug verbosity $ "Dependencies loaded: "
236241
++ intercalate ", " (map prettyShow deps)
237242
let outdatedDeps = listOutdated deps sourcePkgDb
@@ -293,14 +298,15 @@ depsFromFreezeFile verbosity = do
293298
return deps
294299

295300
-- | Read the list of dependencies from the new-style freeze file.
296-
depsFromNewFreezeFile :: Verbosity -> Maybe FilePath -> IO [PackageVersionConstraint]
297-
depsFromNewFreezeFile verbosity mprojectFile = do
301+
depsFromNewFreezeFile :: Verbosity -> HttpTransport -> Compiler -> Platform -> Maybe FilePath -> IO [PackageVersionConstraint]
302+
depsFromNewFreezeFile verbosity httpTransport compiler (Platform arch os) mprojectFile = do
298303
projectRoot <- either throwIO return =<<
299304
findProjectRoot Nothing mprojectFile
300305
let distDirLayout = defaultDistDirLayout projectRoot
301306
{- TODO: Support dist dir override -} Nothing
302-
projectConfig <- runRebuild (distProjectRootDirectory distDirLayout) $
303-
readProjectLocalFreezeConfig verbosity distDirLayout
307+
projectConfig <- runRebuild (distProjectRootDirectory distDirLayout) $ do
308+
pcs <- readProjectLocalFreezeConfig verbosity httpTransport distDirLayout
309+
pure $ instantiateProjectConfigSkeleton os arch (compilerInfo compiler) mempty pcs
304310
let ucnstrs = map fst . projectConfigConstraints . projectConfigShared
305311
$ projectConfig
306312
deps = userConstraintsToDependencies ucnstrs

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

-1
Original file line numberDiff line numberDiff line change
@@ -369,4 +369,3 @@ parseConfig fieldDescrs sectionDescrs fgSectionDescrs empty str =
369369
--
370370
showConfig :: [FieldDescr a] -> [SectionDescr a] -> [FGSectionDescr FG.PrettyFieldGrammar a] -> a -> Disp.Doc
371371
showConfig = ppFieldsAndSections
372-

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

+31-53
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ module Distribution.Client.ProjectConfig (
2929
readGlobalConfig,
3030
readProjectLocalExtraConfig,
3131
readProjectLocalFreezeConfig,
32-
parseProjectConfig,
3332
reportParseResult,
3433
showProjectConfig,
3534
withProjectOrGlobalConfig,
@@ -504,31 +503,33 @@ withProjectOrGlobalConfig' verbosity globalConfigFlag with without = do
504503
-- file if any, plus other global config.
505504
--
506505
readProjectConfig :: Verbosity
506+
-> HttpTransport
507507
-> Flag FilePath
508508
-> DistDirLayout
509-
-> Rebuild ProjectConfig
510-
readProjectConfig verbosity configFileFlag distDirLayout = do
511-
global <- readGlobalConfig verbosity configFileFlag
512-
local <- readProjectLocalConfigOrDefault verbosity distDirLayout
513-
freeze <- readProjectLocalFreezeConfig verbosity distDirLayout
514-
extra <- readProjectLocalExtraConfig verbosity distDirLayout
509+
-> Rebuild ProjectConfigSkeleton
510+
readProjectConfig verbosity httpTransport configFileFlag distDirLayout = do
511+
global <- singletonProjectConfigSkeleton <$> readGlobalConfig verbosity configFileFlag
512+
local <- readProjectLocalConfigOrDefault verbosity httpTransport distDirLayout
513+
freeze <- readProjectLocalFreezeConfig verbosity httpTransport distDirLayout
514+
extra <- readProjectLocalExtraConfig verbosity httpTransport distDirLayout
515515
return (global <> local <> freeze <> extra)
516516

517517

518518
-- | Reads an explicit @cabal.project@ file in the given project root dir,
519519
-- or returns the default project config for an implicitly defined project.
520520
--
521521
readProjectLocalConfigOrDefault :: Verbosity
522+
-> HttpTransport
522523
-> DistDirLayout
523-
-> Rebuild ProjectConfig
524-
readProjectLocalConfigOrDefault verbosity distDirLayout = do
524+
-> Rebuild ProjectConfigSkeleton
525+
readProjectLocalConfigOrDefault verbosity httpTransport distDirLayout = do
525526
usesExplicitProjectRoot <- liftIO $ doesFileExist projectFile
526527
if usesExplicitProjectRoot
527528
then do
528-
readProjectFile verbosity distDirLayout "" "project file"
529+
readProjectFileSkeleton verbosity httpTransport distDirLayout "" "project file"
529530
else do
530531
monitorFiles [monitorNonExistentFile projectFile]
531-
return defaultImplicitProjectConfig
532+
return (singletonProjectConfigSkeleton defaultImplicitProjectConfig)
532533

533534
where
534535
projectFile :: FilePath
@@ -547,66 +548,43 @@ readProjectLocalConfigOrDefault verbosity distDirLayout = do
547548
-- or returns empty. This file gets written by @cabal configure@, or in
548549
-- principle can be edited manually or by other tools.
549550
--
550-
readProjectLocalExtraConfig :: Verbosity -> DistDirLayout
551-
-> Rebuild ProjectConfig
552-
readProjectLocalExtraConfig verbosity distDirLayout =
553-
readProjectFile verbosity distDirLayout "local"
551+
readProjectLocalExtraConfig :: Verbosity -> HttpTransport -> DistDirLayout
552+
-> Rebuild ProjectConfigSkeleton
553+
readProjectLocalExtraConfig verbosity httpTransport distDirLayout =
554+
readProjectFileSkeleton verbosity httpTransport distDirLayout "local"
554555
"project local configuration file"
555556

556557
-- | Reads a @cabal.project.freeze@ file in the given project root dir,
557558
-- or returns empty. This file gets written by @cabal freeze@, or in
558559
-- principle can be edited manually or by other tools.
559560
--
560-
readProjectLocalFreezeConfig :: Verbosity -> DistDirLayout
561-
-> Rebuild ProjectConfig
562-
readProjectLocalFreezeConfig verbosity distDirLayout =
563-
readProjectFile verbosity distDirLayout "freeze"
561+
readProjectLocalFreezeConfig :: Verbosity -> HttpTransport ->DistDirLayout
562+
-> Rebuild ProjectConfigSkeleton
563+
readProjectLocalFreezeConfig verbosity httpTransport distDirLayout =
564+
readProjectFileSkeleton verbosity httpTransport distDirLayout "freeze"
564565
"project freeze file"
565566

566-
-- | Reads a named config file in the given project root dir, or returns empty.
567+
-- | Reads a named extended (with imports and conditionals) config file in the given project root dir, or returns empty.
567568
--
568-
readProjectFile :: Verbosity
569-
-> DistDirLayout
570-
-> String
571-
-> String
572-
-> Rebuild ProjectConfig
573-
readProjectFile verbosity DistDirLayout{distProjectFile}
569+
readProjectFileSkeleton :: Verbosity -> HttpTransport -> DistDirLayout -> String -> String -> Rebuild ProjectConfigSkeleton
570+
readProjectFileSkeleton verbosity httpTransport DistDirLayout{distProjectFile, distDownloadSrcDirectory}
574571
extensionName extensionDescription = do
575572
exists <- liftIO $ doesFileExist extensionFile
576573
if exists
577574
then do monitorFiles [monitorFileHashed extensionFile]
578-
addProjectFileProvenance <$> liftIO readExtensionFile
575+
pcs <- liftIO readExtensionFile
576+
monitorFiles $ map monitorFileHashed (projectSkeletonImports pcs)
577+
pure pcs
579578
else do monitorFiles [monitorNonExistentFile extensionFile]
580579
return mempty
581580
where
582-
extensionFile :: FilePath
583581
extensionFile = distProjectFile extensionName
584582

585-
readExtensionFile :: IO ProjectConfig
586583
readExtensionFile =
587584
reportParseResult verbosity extensionDescription extensionFile
588-
. (parseProjectConfig extensionFile)
585+
=<< parseProjectSkeleton distDownloadSrcDirectory httpTransport verbosity [] extensionFile
589586
=<< BS.readFile extensionFile
590587

591-
addProjectFileProvenance :: ProjectConfig -> ProjectConfig
592-
addProjectFileProvenance config =
593-
config {
594-
projectConfigProvenance =
595-
Set.insert (Explicit extensionFile) (projectConfigProvenance config)
596-
}
597-
598-
599-
-- | Parse the 'ProjectConfig' format.
600-
--
601-
-- For the moment this is implemented in terms of parsers for legacy
602-
-- configuration types, plus a conversion.
603-
--
604-
parseProjectConfig :: FilePath -> BS.ByteString -> OldParser.ParseResult ProjectConfig
605-
parseProjectConfig source content =
606-
convertLegacyProjectConfig <$>
607-
(parseLegacyProjectConfig source content)
608-
609-
610588
-- | Render the 'ProjectConfig' format.
611589
--
612590
-- For the moment this is implemented in terms of a pretty printer for the
@@ -647,12 +625,12 @@ readGlobalConfig verbosity configFileFlag = do
647625
monitorFiles [monitorFileHashed configFile]
648626
return (convertLegacyGlobalConfig config)
649627

650-
reportParseResult :: Verbosity -> String -> FilePath -> OldParser.ParseResult a -> IO a
628+
reportParseResult :: Verbosity -> String -> FilePath -> OldParser.ParseResult ProjectConfigSkeleton -> IO ProjectConfigSkeleton
651629
reportParseResult verbosity _filetype filename (OldParser.ParseOk warnings x) = do
652-
unless (null warnings) $
653-
let msg = unlines (map (OldParser.showPWarning filename) warnings)
630+
unless (null warnings) $
631+
let msg = unlines (map (OldParser.showPWarning (intercalate ", " $ filename : projectSkeletonImports x)) warnings)
654632
in warn verbosity msg
655-
return x
633+
return x
656634
reportParseResult verbosity filetype filename (OldParser.ParseFailed err) =
657635
let (line, msg) = OldParser.locatedErrorMsg err
658636
in die' verbosity $ "Error parsing " ++ filetype ++ " " ++ filename

0 commit comments

Comments
 (0)