cabal-install-solver-3.12.1.0: The command-line interface for Cabal and Hackage.
Safe HaskellNone
LanguageHaskell2010

Distribution.Solver.Types.ProjectConfigPath

Synopsis

Project Config Path Manipulation

newtype ProjectConfigPath #

Path to a configuration file, either a singleton project root, or a longer list representing a path to an import. The path is a non-empty list that we build up by prepending relative imports with consProjectConfigPath.

An import can be a URI, such as a stackage cabal.config, but we do not support URIs in the middle of the path, URIs that import other URIs, or URIs that import local files.

List elements are relative to each other but once canonicalized, elements are relative to the directory of the project root.

Instances

Instances details
Structured ProjectConfigPath # 
Instance details

Defined in Distribution.Solver.Types.ProjectConfigPath

Binary ProjectConfigPath # 
Instance details

Defined in Distribution.Solver.Types.ProjectConfigPath

Generic ProjectConfigPath # 
Instance details

Defined in Distribution.Solver.Types.ProjectConfigPath

Associated Types

type Rep ProjectConfigPath 
Instance details

Defined in Distribution.Solver.Types.ProjectConfigPath

type Rep ProjectConfigPath = D1 ('MetaData "ProjectConfigPath" "Distribution.Solver.Types.ProjectConfigPath" "cabal-install-solver-3.12.1.0-BcLJX5rVQuejx1qrKAH5j" 'True) (C1 ('MetaCons "ProjectConfigPath" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (NonEmpty FilePath))))
Show ProjectConfigPath # 
Instance details

Defined in Distribution.Solver.Types.ProjectConfigPath

Eq ProjectConfigPath # 
Instance details

Defined in Distribution.Solver.Types.ProjectConfigPath

Ord ProjectConfigPath # 
Instance details

Defined in Distribution.Solver.Types.ProjectConfigPath

type Rep ProjectConfigPath # 
Instance details

Defined in Distribution.Solver.Types.ProjectConfigPath

type Rep ProjectConfigPath = D1 ('MetaData "ProjectConfigPath" "Distribution.Solver.Types.ProjectConfigPath" "cabal-install-solver-3.12.1.0-BcLJX5rVQuejx1qrKAH5j" 'True) (C1 ('MetaCons "ProjectConfigPath" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (NonEmpty FilePath))))

projectConfigPathRoot :: ProjectConfigPath -> FilePath #

The root of the path, the project itself.

nullProjectConfigPath :: ProjectConfigPath #

Used by some tests as a dummy "unused" project root.

consProjectConfigPath :: FilePath -> ProjectConfigPath -> ProjectConfigPath #

Prepends the path of the importee to the importer path.

Messages

docProjectConfigPath :: ProjectConfigPath -> Doc #

Renders the path like this; D.config imported by: C.config imported by: B.config imported by: A.project >>> render . docProjectConfigPath $ ProjectConfigPath $ "D.config" :| ["C.config", "B.config", "A.project"] "D.confign imported by: C.confign imported by: B.confign imported by: A.project"

docProjectConfigPaths :: [ProjectConfigPath] -> Doc #

Renders the paths as a list without showing which path imports another, like this; - cabal.project - project-cabal/constraints.config - project-cabal/ghc-latest.config - project-cabal/ghc-options.config - project-cabal/pkgs.config - project-cabalpkgsbenchmarks.config - project-cabalpkgsbuildinfo.config - project-cabalpkgscabal.config - project-cabalpkgsinstall.config - project-cabalpkgsintegration-tests.config - project-cabalpkgstests.config

>>> :{
  do
    let ps =
             [ ProjectConfigPath ("cabal.project" :| [])
             , ProjectConfigPath ("project-cabal/constraints.config" :| ["cabal.project"])
             , ProjectConfigPath ("project-cabal/ghc-latest.config" :| ["cabal.project"])
             , ProjectConfigPath ("project-cabal/ghc-options.config" :| ["cabal.project"])
             , ProjectConfigPath ("project-cabal/pkgs.config" :| ["cabal.project"])
             , ProjectConfigPath ("project-cabal/pkgs/benchmarks.config" :| ["project-cabal/pkgs.config","cabal.project"])
             , ProjectConfigPath ("project-cabal/pkgs/buildinfo.config" :| ["project-cabal/pkgs.config","cabal.project"])
             , ProjectConfigPath ("project-cabal/pkgs/cabal.config" :| ["project-cabal/pkgs.config","cabal.project"])
             , ProjectConfigPath ("project-cabal/pkgs/install.config" :| ["project-cabal/pkgs.config","cabal.project"])
             , ProjectConfigPath ("project-cabal/pkgs/integration-tests.config" :| ["project-cabal/pkgs.config","cabal.project"])
             , ProjectConfigPath ("project-cabal/pkgs/tests.config" :| ["project-cabal/pkgs.config","cabal.project"])
             ]
    return . render $ docProjectConfigPaths ps
:}
"- cabal.project\n- project-cabal/constraints.config\n- project-cabal/ghc-latest.config\n- project-cabal/ghc-options.config\n- project-cabal/pkgs.config\n- project-cabal/pkgs/benchmarks.config\n- project-cabal/pkgs/buildinfo.config\n- project-cabal/pkgs/cabal.config\n- project-cabal/pkgs/install.config\n- project-cabal/pkgs/integration-tests.config\n- project-cabal/pkgs/tests.config"

cyclicalImportMsg :: ProjectConfigPath -> Doc #

A message for a cyclical import, assuming the head of the path is the duplicate.

Checks and Normalization

isCyclicConfigPath :: ProjectConfigPath -> Bool #

Check if the path has duplicates. A cycle of imports is not allowed. This check should only be done after the path has been canonicalized with canonicalizeConfigPath. This is because the import path may contain paths that are the same in relation to their importers but different in relation to the project root directory.

canonicalizeConfigPath :: FilePath -> ProjectConfigPath -> IO ProjectConfigPath #

Normalizes and canonicalizes a path removing . and '..' indirections. Makes the path relative to the given directory (typically the project root) instead of relative to the file it was imported from.

It converts paths like this: └─ hops-0.project └─ hops/hops-1.config └─ ../hops-2.config └─ hops/hops-3.config └─ ../hops-4.config └─ hops/hops-5.config └─ ../hops-6.config └─ hops/hops-7.config └─ ../hops-8.config └─ hops/hops-9.config

Into paths like this: └─ hops-0.project └─ hops/hops-1.config └─ hops-2.config └─ hops/hops-3.config └─ hops-4.config └─ hops/hops-5.config └─ hops-6.config └─ hops/hops-7.config └─ hops-8.config └─ hops/hops-9.config

That way we have hops-8.config instead of .hops..hops..hops..hops../hops-8.config.

Let's see how canonicalizePath works that is used in the implementation then we'll see how canonicalizeConfigPath works.

>>> let d = testDir
>>> makeRelative d <$> canonicalizePath (d </> "hops/../hops/../hops/../hops/../hops-8.config")
"hops-8.config"
>>> let d = testDir
>>> p <- canonicalizeConfigPath d (ProjectConfigPath $ (d </> "hops/../hops/../hops/../hops/../hops-8.config") :| [])
>>> render $ docProjectConfigPath p
"hops-8.config"
>>> :{
  do
    let expected = unlines
          [ "hops/hops-9.config"
          , "  imported by: hops-8.config"
          , "  imported by: hops/hops-7.config"
          , "  imported by: hops-6.config"
          , "  imported by: hops/hops-5.config"
          , "  imported by: hops-4.config"
          , "  imported by: hops/hops-3.config"
          , "  imported by: hops-2.config"
          , "  imported by: hops/hops-1.config"
          , "  imported by: hops-0.project"
          ]
    let d = testDir
    let configPath = ProjectConfigPath ("hops/hops-9.config" :|
          [ "../hops-8.config"
          , "hops/hops-7.config"
          , "../hops-6.config"
          , "hops/hops-5.config"
          , "../hops-4.config"
          , "hops/hops-3.config"
          , "../hops-2.config"
          , "hops/hops-1.config"
          , d </> "hops-0.project"])
    p <- canonicalizeConfigPath d configPath
    return $ expected == render (docProjectConfigPath p) ++ "\n"
:}
True