|
1 | 1 | {-# LANGUAGE ConstraintKinds #-}
|
2 | 2 | {-# LANGUAGE DataKinds #-}
|
3 | 3 | {-# LANGUAGE DeriveGeneric #-}
|
| 4 | +{-# LANGUAGE MultiWayIf #-} |
4 | 5 | {-# LANGUAGE NamedFieldPuns #-}
|
5 | 6 | {-# LANGUAGE RecordWildCards #-}
|
6 | 7 | {-# LANGUAGE ScopedTypeVariables #-}
|
@@ -33,6 +34,7 @@ module Distribution.Client.ProjectConfig.Legacy
|
33 | 34 | ) where
|
34 | 35 |
|
35 | 36 | import Data.Coerce (coerce)
|
| 37 | +import Data.IORef |
36 | 38 | import Distribution.Client.Compat.Prelude
|
37 | 39 |
|
38 | 40 | import Distribution.Types.Flag (FlagName, parsecFlagAssignment)
|
@@ -137,7 +139,8 @@ import Distribution.Types.CondTree
|
137 | 139 | )
|
138 | 140 | import Distribution.Types.SourceRepo (RepoType)
|
139 | 141 | import Distribution.Utils.NubList
|
140 |
| - ( fromNubList |
| 142 | + ( NubList |
| 143 | + , fromNubList |
141 | 144 | , overNubList
|
142 | 145 | , toNubList
|
143 | 146 | )
|
@@ -244,41 +247,51 @@ parseProject
|
244 | 247 | parseProject rootPath cacheDir httpTransport verbosity configToParse = do
|
245 | 248 | let (dir, projectFileName) = splitFileName rootPath
|
246 | 249 | projectDir <- makeAbsolute dir
|
247 |
| - projectPath <- canonicalizeConfigPath projectDir (ProjectConfigPath $ projectFileName :| []) |
248 |
| - parseProjectSkeleton cacheDir httpTransport verbosity projectDir projectPath configToParse |
| 250 | + projectPath@(ProjectConfigPath (canonicalRoot :| _)) <- canonicalizeConfigPath projectDir (ProjectConfigPath $ projectFileName :| []) |
| 251 | + importsBy <- newIORef $ toNubList [(canonicalRoot, projectPath)] |
| 252 | + parseProjectSkeleton cacheDir httpTransport verbosity importsBy projectDir projectPath configToParse |
249 | 253 |
|
250 | 254 | parseProjectSkeleton
|
251 | 255 | :: FilePath
|
252 | 256 | -> HttpTransport
|
253 | 257 | -> Verbosity
|
| 258 | + -> IORef (NubList (FilePath, ProjectConfigPath)) |
| 259 | + -- ^ The imports seen so far, useful for reporting on duplicates and to detect duplicates that are not cycles |
254 | 260 | -> FilePath
|
255 | 261 | -- ^ The directory of the project configuration, typically the directory of cabal.project
|
256 | 262 | -> ProjectConfigPath
|
257 | 263 | -- ^ The path of the file being parsed, either the root or an import
|
258 | 264 | -> ProjectConfigToParse
|
259 | 265 | -- ^ The contents of the file to parse
|
260 | 266 | -> IO (ParseResult ProjectConfigSkeleton)
|
261 |
| -parseProjectSkeleton cacheDir httpTransport verbosity projectDir source (ProjectConfigToParse bs) = |
| 267 | +parseProjectSkeleton cacheDir httpTransport verbosity importsBy projectDir source (ProjectConfigToParse bs) = |
262 | 268 | (sanityWalkPCS False =<<) <$> liftPR (go []) (ParseUtils.readFields bs)
|
263 | 269 | where
|
264 | 270 | go :: [ParseUtils.Field] -> [ParseUtils.Field] -> IO (ParseResult ProjectConfigSkeleton)
|
265 | 271 | go acc (x : xs) = case x of
|
266 | 272 | (ParseUtils.F _ "import" importLoc) -> do
|
267 | 273 | let importLocPath = importLoc `consProjectConfigPath` source
|
268 | 274 |
|
269 |
| - -- Once we canonicalize the import path, we can check for cyclical imports |
270 |
| - normLocPath <- canonicalizeConfigPath projectDir importLocPath |
| 275 | + -- Once we canonicalize the import path, we can check for cyclical and duplicate imports |
| 276 | + normLocPath@(ProjectConfigPath (uniqueImport :| _)) <- canonicalizeConfigPath projectDir importLocPath |
| 277 | + seenImportsBy@(fmap fst -> seenImports) <- fromNubList <$> atomicModifyIORef' importsBy (\ibs -> (toNubList [(uniqueImport, normLocPath)] <> ibs, ibs)) |
271 | 278 |
|
272 | 279 | debug verbosity $ "\nimport path, normalized\n=======================\n" ++ render (docProjectConfigPath normLocPath)
|
273 |
| - |
274 |
| - if isCyclicConfigPath normLocPath |
275 |
| - then pure . parseFail $ ParseUtils.FromString (render $ cyclicalImportMsg normLocPath) Nothing |
276 |
| - else do |
277 |
| - normSource <- canonicalizeConfigPath projectDir source |
278 |
| - let fs = (\z -> CondNode z [normLocPath] mempty) <$> fieldsToConfig normSource (reverse acc) |
279 |
| - res <- parseProjectSkeleton cacheDir httpTransport verbosity projectDir importLocPath . ProjectConfigToParse =<< fetchImportConfig normLocPath |
280 |
| - rest <- go [] xs |
281 |
| - pure . fmap mconcat . sequence $ [fs, res, rest] |
| 280 | + debug verbosity "\nseen unique paths\n=================" |
| 281 | + mapM_ (debug verbosity) seenImports |
| 282 | + debug verbosity "\n" |
| 283 | + |
| 284 | + if |
| 285 | + | isCyclicConfigPath normLocPath -> |
| 286 | + pure . parseFail $ ParseUtils.FromString (render $ cyclicalImportMsg normLocPath) Nothing |
| 287 | + | uniqueImport `elem` seenImports -> do |
| 288 | + pure . parseFail $ ParseUtils.FromString (render $ duplicateImportMsg uniqueImport normLocPath seenImportsBy) Nothing |
| 289 | + | otherwise -> do |
| 290 | + normSource <- canonicalizeConfigPath projectDir source |
| 291 | + let fs = (\z -> CondNode z [normLocPath] mempty) <$> fieldsToConfig normSource (reverse acc) |
| 292 | + res <- parseProjectSkeleton cacheDir httpTransport verbosity importsBy projectDir importLocPath . ProjectConfigToParse =<< fetchImportConfig normLocPath |
| 293 | + rest <- go [] xs |
| 294 | + pure . fmap mconcat . sequence $ [fs, res, rest] |
282 | 295 | (ParseUtils.Section l "if" p xs') -> do
|
283 | 296 | subpcs <- go [] xs'
|
284 | 297 | let fs = singletonProjectConfigSkeleton <$> fieldsToConfig source (reverse acc)
|
|
0 commit comments