|
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)
|
@@ -245,41 +247,51 @@ parseProject
|
245 | 247 | parseProject rootPath cacheDir httpTransport verbosity configToParse = do
|
246 | 248 | let (dir, projectFileName) = splitFileName rootPath
|
247 | 249 | projectDir <- makeAbsolute dir
|
248 |
| - projectPath <- canonicalizeConfigPath projectDir (ProjectConfigPath $ projectFileName :| []) |
249 |
| - parseProjectSkeleton cacheDir httpTransport verbosity projectDir projectPath configToParse |
| 250 | + projectPath@(ProjectConfigPath (canonicalRoot :| _)) <- canonicalizeConfigPath projectDir (ProjectConfigPath $ projectFileName :| []) |
| 251 | + importsBy :: IORef [(FilePath, ProjectConfigPath)] <- newIORef [(canonicalRoot, projectPath)] |
| 252 | + parseProjectSkeleton cacheDir httpTransport verbosity importsBy projectDir projectPath configToParse |
250 | 253 |
|
251 | 254 | parseProjectSkeleton
|
252 | 255 | :: FilePath
|
253 | 256 | -> HttpTransport
|
254 | 257 | -> Verbosity
|
| 258 | + -> IORef [(FilePath, ProjectConfigPath)] |
255 | 259 | -> FilePath
|
256 | 260 | -- ^ The directory of the project configuration, typically the directory of cabal.project
|
257 | 261 | -> ProjectConfigPath
|
258 | 262 | -- ^ The path of the file being parsed, either the root or an import
|
259 | 263 | -> ProjectConfigToParse
|
260 | 264 | -- ^ The contents of the file to parse
|
261 | 265 | -> IO (ParseResult ProjectConfigSkeleton)
|
262 |
| -parseProjectSkeleton cacheDir httpTransport verbosity projectDir source (ProjectConfigToParse bs) = |
| 266 | +parseProjectSkeleton cacheDir httpTransport verbosity importsBy projectDir source (ProjectConfigToParse bs) = |
263 | 267 | (sanityWalkPCS False =<<) <$> liftPR (go []) (ParseUtils.readFields bs)
|
264 | 268 | where
|
265 | 269 | go :: [ParseUtils.Field] -> [ParseUtils.Field] -> IO (ParseResult ProjectConfigSkeleton)
|
266 | 270 | go acc (x : xs) = case x of
|
267 | 271 | (ParseUtils.F _ "import" importLoc) -> do
|
268 | 272 | let importLocPath = importLoc `consProjectConfigPath` source
|
269 | 273 |
|
270 |
| - -- Once we canonicalize the import path, we can check for cyclical imports |
271 |
| - normLocPath <- canonicalizeConfigPath projectDir importLocPath |
| 274 | + -- Once we canonicalize the import path, we can check for cyclical and duplicate imports |
| 275 | + normLocPath@(ProjectConfigPath (uniqueImport :| _)) <- canonicalizeConfigPath projectDir importLocPath |
| 276 | + seenImportsBy@(fmap fst -> seenImports) <- atomicModifyIORef' importsBy (\ibs -> (nub $ (uniqueImport, normLocPath) : ibs, ibs)) |
272 | 277 |
|
273 | 278 | debug verbosity $ "\nimport path, normalized\n=======================\n" ++ render (docProjectConfigPath normLocPath)
|
274 |
| - |
275 |
| - if isCyclicConfigPath normLocPath |
276 |
| - then pure . parseFail $ ParseUtils.FromString (render $ cyclicalImportMsg normLocPath) Nothing |
277 |
| - else do |
278 |
| - normSource <- canonicalizeConfigPath projectDir source |
279 |
| - let fs = (\z -> CondNode z [normLocPath] mempty) <$> fieldsToConfig normSource (reverse acc) |
280 |
| - res <- parseProjectSkeleton cacheDir httpTransport verbosity projectDir importLocPath . ProjectConfigToParse =<< fetchImportConfig normLocPath |
281 |
| - rest <- go [] xs |
282 |
| - pure . fmap mconcat . sequence $ [fs, res, rest] |
| 279 | + debug verbosity "\nseen unique paths\n=================" |
| 280 | + mapM_ (debug verbosity . fst) seenImportsBy |
| 281 | + debug verbosity "\n" |
| 282 | + |
| 283 | + if |
| 284 | + | isCyclicConfigPath normLocPath -> |
| 285 | + pure . parseFail $ ParseUtils.FromString (render $ cyclicalImportMsg normLocPath) Nothing |
| 286 | + | uniqueImport `elem` seenImports -> do |
| 287 | + let dupImportsBy = filter ((uniqueImport ==) . fst) seenImportsBy |
| 288 | + pure . parseFail $ ParseUtils.FromString (render $ duplicateImportMsg uniqueImport normLocPath dupImportsBy) 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] |
283 | 295 | (ParseUtils.Section l "if" p xs') -> do
|
284 | 296 | subpcs <- go [] xs'
|
285 | 297 | let fs = singletonProjectConfigSkeleton <$> fieldsToConfig source (reverse acc)
|
|
0 commit comments