|
2 | 2 | {-# LANGUAGE DataKinds #-}
|
3 | 3 | {-# LANGUAGE DeriveGeneric #-}
|
4 | 4 | {-# LANGUAGE LambdaCase #-}
|
| 5 | +{-# LANGUAGE MultiWayIf #-} |
5 | 6 | {-# LANGUAGE NamedFieldPuns #-}
|
6 | 7 | {-# LANGUAGE RecordWildCards #-}
|
7 | 8 | {-# LANGUAGE ScopedTypeVariables #-}
|
@@ -35,6 +36,7 @@ module Distribution.Client.ProjectConfig.Legacy
|
35 | 36 | ) where
|
36 | 37 |
|
37 | 38 | import Data.Coerce (coerce)
|
| 39 | +import Data.IORef |
38 | 40 | import Distribution.Client.Compat.Prelude
|
39 | 41 |
|
40 | 42 | import Distribution.Types.Flag (FlagName, parsecFlagAssignment)
|
@@ -142,7 +144,8 @@ import Distribution.Types.CondTree
|
142 | 144 | )
|
143 | 145 | import Distribution.Types.SourceRepo (RepoType)
|
144 | 146 | import Distribution.Utils.NubList
|
145 |
| - ( fromNubList |
| 147 | + ( NubList |
| 148 | + , fromNubList |
146 | 149 | , overNubList
|
147 | 150 | , toNubList
|
148 | 151 | )
|
@@ -260,45 +263,55 @@ parseProject rootPath cacheDir httpTransport verbosity configToParse =
|
260 | 263 | do
|
261 | 264 | let (dir, projectFileName) = splitFileName rootPath
|
262 | 265 | projectDir <- makeAbsolute dir
|
263 |
| - projectPath <- canonicalizeConfigPath projectDir (ProjectConfigPath $ projectFileName :| []) |
264 |
| - parseProjectSkeleton cacheDir httpTransport verbosity projectDir projectPath configToParse |
| 266 | + projectPath@(ProjectConfigPath (canonicalRoot :| _)) <- canonicalizeConfigPath projectDir (ProjectConfigPath $ projectFileName :| []) |
| 267 | + importsBy <- newIORef $ toNubList [(canonicalRoot, projectPath)] |
| 268 | + parseProjectSkeleton cacheDir httpTransport verbosity importsBy projectDir projectPath configToParse |
265 | 269 | -- NOTE: Reverse the warnings so they are in line number order.
|
266 | 270 | <&> \case ProjectParseOk ws x -> ProjectParseOk (reverse ws) x; x -> x
|
267 | 271 |
|
268 | 272 | parseProjectSkeleton
|
269 | 273 | :: FilePath
|
270 | 274 | -> HttpTransport
|
271 | 275 | -> Verbosity
|
| 276 | + -> IORef (NubList (FilePath, ProjectConfigPath)) |
| 277 | + -- ^ The imports seen so far, used to report on cycles and duplicates and to detect duplicates that are not cycles |
272 | 278 | -> FilePath
|
273 | 279 | -- ^ The directory of the project configuration, typically the directory of cabal.project
|
274 | 280 | -> ProjectConfigPath
|
275 | 281 | -- ^ The path of the file being parsed, either the root or an import
|
276 | 282 | -> ProjectConfigToParse
|
277 | 283 | -- ^ The contents of the file to parse
|
278 | 284 | -> IO (ProjectParseResult ProjectConfigSkeleton)
|
279 |
| -parseProjectSkeleton cacheDir httpTransport verbosity projectDir source (ProjectConfigToParse bs) = |
| 285 | +parseProjectSkeleton cacheDir httpTransport verbosity importsBy projectDir source (ProjectConfigToParse bs) = |
280 | 286 | (sanityWalkPCS False =<<) <$> liftPR source (go []) (ParseUtils.readFields bs)
|
281 | 287 | where
|
282 | 288 | go :: [ParseUtils.Field] -> [ParseUtils.Field] -> IO (ProjectParseResult ProjectConfigSkeleton)
|
283 | 289 | go acc (x : xs) = case x of
|
284 | 290 | (ParseUtils.F _ "import" importLoc) -> do
|
285 | 291 | let importLocPath = importLoc `consProjectConfigPath` source
|
286 | 292 |
|
287 |
| - -- Once we canonicalize the import path, we can check for cyclical imports |
| 293 | + -- Once we canonicalize the import path, we can check for cyclical and duplicate imports |
288 | 294 | normSource <- canonicalizeConfigPath projectDir source
|
289 |
| - normLocPath <- canonicalizeConfigPath projectDir importLocPath |
| 295 | + normLocPath@(ProjectConfigPath (uniqueImport :| _)) <- canonicalizeConfigPath projectDir importLocPath |
| 296 | + seenImportsBy@(fmap fst -> seenImports) <- fromNubList <$> atomicModifyIORef' importsBy (\ibs -> (toNubList [(uniqueImport, normLocPath)] <> ibs, ibs)) |
290 | 297 | debug verbosity $ "\nimport path, normalized\n=======================\n" ++ render (docProjectConfigPath normLocPath)
|
291 |
| - |
292 |
| - if isCyclicConfigPath normLocPath |
293 |
| - then pure . projectParseFail Nothing (Just normSource) $ ParseUtils.FromString (render $ cyclicalImportMsg normLocPath) Nothing |
294 |
| - else do |
295 |
| - when |
296 |
| - (isUntrimmedUriConfigPath importLocPath) |
297 |
| - (noticeDoc verbosity $ untrimmedUriImportMsg (Disp.text "Warning:") importLocPath) |
298 |
| - let fs = (\z -> CondNode z [normLocPath] mempty) <$> fieldsToConfig normSource (reverse acc) |
299 |
| - res <- parseProjectSkeleton cacheDir httpTransport verbosity projectDir importLocPath . ProjectConfigToParse =<< fetchImportConfig normLocPath |
300 |
| - rest <- go [] xs |
301 |
| - pure . fmap mconcat . sequence $ [projectParse Nothing normSource fs, res, rest] |
| 298 | + debug verbosity "\nseen unique paths\n=================" |
| 299 | + mapM_ (debug verbosity) seenImports |
| 300 | + debug verbosity "\n" |
| 301 | + |
| 302 | + if |
| 303 | + | isCyclicConfigPath normLocPath -> |
| 304 | + pure . projectParseFail Nothing (Just normSource) $ ParseUtils.FromString (render $ cyclicalImportMsg normLocPath) Nothing |
| 305 | + | uniqueImport `elem` seenImports -> do |
| 306 | + pure . parseFail $ ParseUtils.FromString (render $ duplicateImportMsg uniqueImport normLocPath seenImportsBy) Nothing |
| 307 | + | otherwise -> do |
| 308 | + when |
| 309 | + (isUntrimmedUriConfigPath importLocPath) |
| 310 | + (noticeDoc verbosity $ untrimmedUriImportMsg (Disp.text "Warning:") importLocPath) |
| 311 | + let fs = (\z -> CondNode z [normLocPath] mempty) <$> fieldsToConfig normSource (reverse acc) |
| 312 | + res <- parseProjectSkeleton cacheDir httpTransport verbosity importsBy projectDir importLocPath . ProjectConfigToParse =<< fetchImportConfig normLocPath |
| 313 | + rest <- go [] xs |
| 314 | + pure . fmap mconcat . sequence $ [projectParse Nothing normSource fs, res, rest] |
302 | 315 | (ParseUtils.Section l "if" p xs') -> do
|
303 | 316 | normSource <- canonicalizeConfigPath projectDir source
|
304 | 317 | subpcs <- go [] xs'
|
|
0 commit comments