@@ -34,6 +34,7 @@ import System.FilePath
3434import qualified Data.List.NonEmpty as NE
3535import Distribution.Solver.Modular.Version (VR )
3636import Distribution.Pretty (prettyShow )
37+ import Distribution.Utils.String (trim )
3738import Text.PrettyPrint
3839import Distribution.Simple.Utils (ordNub )
3940
@@ -98,9 +99,13 @@ instance Structured ProjectConfigPath
9899-- >>> render . docProjectConfigPath $ ProjectConfigPath $ "D.config" :| ["C.config", "B.config", "A.project"]
99100-- "D.config\n imported by: C.config\n imported by: B.config\n imported by: A.project"
100101docProjectConfigPath :: ProjectConfigPath -> Doc
101- docProjectConfigPath (ProjectConfigPath (p :| [] )) = text p
102- docProjectConfigPath (ProjectConfigPath (p :| ps)) = vcat $
103- text p : [ text " " <+> text " imported by:" <+> text l | l <- ps ]
102+ docProjectConfigPath (ProjectConfigPath (p :| [] )) = quoteUntrimmed p
103+ docProjectConfigPath (ProjectConfigPath (p :| ps)) = vcat $ quoteUntrimmed p :
104+ [ text " " <+> text " imported by:" <+> quoteUntrimmed l | l <- ps ]
105+
106+ -- | If the path has leading or trailing spaces then show it quoted.
107+ quoteUntrimmed :: FilePath -> Doc
108+ quoteUntrimmed s = if trim s /= s then quotes (text s) else text s
104109
105110-- | Renders the paths as a list without showing which path imports another,
106111-- like this;
@@ -196,7 +201,7 @@ unconsProjectConfigPath ps = fmap ProjectConfigPath <$> NE.uncons (coerce ps)
196201makeRelativeConfigPath :: FilePath -> ProjectConfigPath -> ProjectConfigPath
197202makeRelativeConfigPath dir (ProjectConfigPath p) =
198203 ProjectConfigPath
199- $ (\ segment -> (if isURI segment then segment else makeRelative dir segment))
204+ $ (\ segment@ (trim -> trimSegment) -> (if isURI trimSegment then trimSegment else makeRelative dir segment))
200205 <$> p
201206
202207-- | Normalizes and canonicalizes a path removing '.' and '..' indirections.
@@ -273,11 +278,25 @@ makeRelativeConfigPath dir (ProjectConfigPath p) =
273278-- return $ expected == render (docProjectConfigPath p) ++ "\n"
274279-- :}
275280-- True
281+ --
282+ -- "A string is a valid URL potentially surrounded by spaces if, after stripping leading and trailing whitespace from it, it is a valid URL."
283+ -- [W3C/HTML5/URLs](https://www.w3.org/TR/2010/WD-html5-20100624/urls.html)
284+ --
285+ -- Trailing spaces for @ProjectConfigPath@ URLs are trimmed.
286+ --
287+ -- >>> p <- canonicalizeConfigPath "" (ProjectConfigPath $ ("https://www.stackage.org/nightly-2024-12-05/cabal.config ") :| [])
288+ -- >>> render $ docProjectConfigPath p
289+ -- "https://www.stackage.org/nightly-2024-12-05/cabal.config"
290+ --
291+ -- >>> let d = testDir
292+ -- >>> p <- canonicalizeConfigPath d (ProjectConfigPath $ ("https://www.stackage.org/nightly-2024-12-05/cabal.config ") :| [d </> "cabal.project"])
293+ -- >>> render $ docProjectConfigPath p
294+ -- "https://www.stackage.org/nightly-2024-12-05/cabal.config\n imported by: cabal.project"
276295canonicalizeConfigPath :: FilePath -> ProjectConfigPath -> IO ProjectConfigPath
277296canonicalizeConfigPath d (ProjectConfigPath p) = do
278- xs <- sequence $ NE. scanr (\ importee -> (>>= \ importer ->
279- if isURI importee
280- then pure importee
297+ xs <- sequence $ NE. scanr (\ importee@ (trim -> trimImportee) -> (>>= \ importer@ (trim -> trimImporter) ->
298+ if isURI trimImportee || isURI trimImporter
299+ then pure trimImportee
281300 else canonicalizePath $ d </> takeDirectory importer </> importee))
282301 (pure " ." ) p
283302 return . makeRelativeConfigPath d . ProjectConfigPath . NE. fromList $ NE. init xs
0 commit comments