Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport #10646: Deduplicate path separator duplicates #10751

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,36 @@ description: {
}
```

<<<<<<< HEAD
=======
Changelogs may also be written in "markdown-frontmatter" format. This is useful
if your description contains braces, which must be escaped with backslashes in
`.cabal` file format. Another benefit of using an `.md` extension with your
changelog is that it will be checked for typos.

The front matter is in YAML syntax, not `.cabal` file syntax, and the file
_must_ begin with a line containing only hyphens.

```markdown
---
synopsis: Add feature xyz
packages: [cabal-install]
prs: 0000
issues: [0000, 0000]
significance: significant
---

- Detail number 1
- Detail number 2

```
The package list must be enclosed in square brackets and comma-separated, but this
isn't needed for `prs` or `issues`; those are free-form and any YAML syntax will
be accepted. Note that the number signs on PR and issue numbers are required in
`.cabal` file syntax, but won't work in markdown-frontmatter syntax because they
signify comments in YAML.

>>>>>>> 30f5d3f21 (Needle in haystack multiline expectations)
Only the `synopsis` and `prs` fields are required, but you should also set the others where applicable.

| Field | Description |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,19 @@
import Data.List.NonEmpty ((<|))
import Network.URI (parseURI)
import System.Directory
import System.FilePath
import System.FilePath hiding (splitPath)
import qualified System.FilePath as FP (splitPath)
import qualified System.FilePath.Posix as Posix
import qualified System.FilePath.Windows as Windows
import qualified Data.List.NonEmpty as NE
import Distribution.Solver.Modular.Version (VR)
import Distribution.Pretty (prettyShow)
import Text.PrettyPrint
<<<<<<< HEAD

Check failure on line 38 in cabal-install-solver/src/Distribution/Solver/Types/ProjectConfigPath.hs

View workflow job for this annotation

GitHub Actions / hlint

Error: Parse error: on input `<<<<<<<' ▫︎ Found: " import Distribution.Pretty (prettyShow)\n import Text.PrettyPrint\n> <<<<<<< HEAD\n =======\n import Distribution.Simple.Utils (ordNub)\n"

Check failure on line 38 in cabal-install-solver/src/Distribution/Solver/Types/ProjectConfigPath.hs

View workflow job for this annotation

GitHub Actions / hlint

Error: Parse error: on input `<<<<<<<' ▫︎ Found: " import Distribution.Pretty (prettyShow)\n import Text.PrettyPrint\n> <<<<<<< HEAD\n =======\n import Distribution.Simple.Utils (ordNub)\n"
=======
import Distribution.Simple.Utils (ordNub)
import Distribution.System (OS(Windows), buildOS)
>>>>>>> d0bd687a2 (Use OS-specific splitPath before comparing projects)

-- | 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
Expand All @@ -45,7 +53,63 @@
-- List elements are relative to each other but once canonicalized, elements are
-- relative to the directory of the project root.
newtype ProjectConfigPath = ProjectConfigPath (NonEmpty FilePath)
<<<<<<< HEAD
deriving (Eq, Ord, Show, Generic)
=======
deriving (Eq, Show, Generic)

-- | Sorts URIs after local file paths and longer file paths after shorter ones
-- as measured by the number of path segments. If still equal, then sorting is
-- lexical.
--
-- The project itself, a single element root path, compared to any of the
-- configuration paths it imports, should always sort first. Comparing one
-- project root path against another is done lexically.
--
-- For comparison purposes, path separators are normalized to the @buildOS@
-- platform's path separator.
--
-- >>> let abFwd = ProjectConfigPath $ "a/b.config" :| []
-- >>> let abBwd = ProjectConfigPath $ "a\\b.config" :| []
-- >>> compare abFwd abBwd
-- EQ
instance Ord ProjectConfigPath where
compare pa@(ProjectConfigPath (NE.toList -> as)) pb@(ProjectConfigPath (NE.toList -> bs)) =
case (as, bs) of
-- There should only ever be one root project path, only one path
-- with length 1. Comparing it to itself should be EQ. Don't assume
-- this though, do a comparison anyway when both sides have length
-- 1. The root path, the project itself, should always be the first
-- path in a sorted listing.
([a], [b]) -> compare (splitPath a) (splitPath b)
([_], _) -> LT
(_, [_]) -> GT

(a:_, b:_) -> case (parseAbsoluteURI a, parseAbsoluteURI b) of
(Just ua, Just ub) -> compare ua ub P.<> compare aImporters bImporters
(Just _, Nothing) -> GT
(Nothing, Just _) -> LT
(Nothing, Nothing) -> compare (splitPath a) (splitPath b) P.<> compare aImporters bImporters
_ ->
compare (length as) (length bs)
P.<> compare (length aPaths) (length bPaths)
P.<> compare aPaths bPaths
where
splitPath = FP.splitPath . normSep where
normSep p =
if buildOS == Windows
then
Windows.joinPath $ Windows.splitDirectories
[if Posix.isPathSeparator c then Windows.pathSeparator else c| c <- p]
else
Posix.joinPath $ Posix.splitDirectories
[if Windows.isPathSeparator c then Posix.pathSeparator else c| c <- p]

aPaths = splitPath <$> as
bPaths = splitPath <$> bs
aImporters = snd $ unconsProjectConfigPath pa
bImporters = snd $ unconsProjectConfigPath pb
>>>>>>> d0bd687a2 (Use OS-specific splitPath before comparing projects)

instance Binary ProjectConfigPath
instance Structured ProjectConfigPath
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ main = setupAndCabalTest . withPackageDb $ do
assertEqual
("executable should have linked with the internal library")
("foo foo myLibFunc internal")
(concatOutput (resultOutput r))
(lineBreaksToSpaces $ resultOutput r)
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ main = setupAndCabalTest . withPackageDb $ do
assertEqual
("executable should have linked with the internal library")
("foo foo myLibFunc internal")
(concatOutput (resultOutput r))
(lineBreaksToSpaces $ resultOutput r)
8 changes: 6 additions & 2 deletions cabal-testsuite/PackageTests/CheckSetup/setup.test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ main = cabalTest $ do
"The dependency 'setup-depends: 'base' does not specify "
++ "an upper bound on the version number"

-- Replace line breaks with spaces in the haystack so that we can search
-- for a string that wraps lines.
let lineBreakBlind = needleHaystack{txHaystack = txContainsId{txFwd = lineBreaksToSpaces}}

-- Asserts for the desired check messages after configure.
assertOutputContains libError1 checkResult
assertOutputContains libError2 checkResult
assertOn lineBreakBlind libError1 checkResult
assertOn lineBreakBlind libError2 checkResult

return ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
When using configuration from:
- cabal-missing-package.project
- missing/pkgs.config
- missing/pkgs/default.config
The following errors occurred:
- The package location 'pkg-doesnt-exist' does not exist.
124 changes: 13 additions & 111 deletions cabal-testsuite/PackageTests/ConditionalAndImport/cabal.test.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Test.Cabal.Prelude

normalizeWindowsOutput :: String -> String
normalizeWindowsOutput = if isWindows then map (\x -> case x of '/' -> '\\'; _ -> x) else id
import Test.Cabal.OutputNormalizer
import Data.Function ((&))
import Data.Functor ((<&>))

main = cabalTest . withRepo "repo" . recordMode RecordMarked $ do
let log = recordHeader . pure
Expand Down Expand Up @@ -79,7 +79,7 @@ main = cabalTest . withRepo "repo" . recordMode RecordMarked $ do
-- +-- etc
log "checking that cyclical check catches a same file name that imports itself"
cyclical4a <- fails $ cabal' "v2-build" [ "--project-file=cyclical-same-filename-out-out-self.project" ]
assertOutputContains (normalizeWindowsOutput "cyclical import of same-filename/cyclical-same-filename-out-out-self.config") cyclical4a
assertOutputContains (normalizePathSeparators "cyclical import of same-filename/cyclical-same-filename-out-out-self.config") cyclical4a

-- +-- cyclical-same-filename-out-out-backback.project
-- +-- cyclical-same-filename-out-out-backback.config
Expand Down Expand Up @@ -111,89 +111,9 @@ main = cabalTest . withRepo "repo" . recordMode RecordMarked $ do
-- +-- hops/hops-9.config (no further imports so not cyclical)
log "checking that imports work skipping into a subfolder and then back out again and again"
hopping <- cabal' "v2-build" [ "--project-file=hops-0.project" ]
assertOutputContains "Configuration is affected by the following files" hopping
assertOutputContains "- hops-0.project" hopping

assertOutputContains
(normalizeWindowsOutput "- hops-2.config \
\ imported by: hops/hops-1.config \
\ imported by: hops-0.project")
hopping

assertOutputContains
(normalizeWindowsOutput "- 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")
hopping

assertOutputContains
(normalizeWindowsOutput "- 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")
hopping

assertOutputContains
(normalizeWindowsOutput "- 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")
hopping

assertOutputContains
(normalizeWindowsOutput "- hops/hops-1.config \
\ imported by: hops-0.project")
hopping

assertOutputContains
(normalizeWindowsOutput "- hops/hops-3.config \
\ imported by: hops-2.config \
\ imported by: hops/hops-1.config \
\ imported by: hops-0.project")
hopping

assertOutputContains
(normalizeWindowsOutput "- 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")
hopping

assertOutputContains
(normalizeWindowsOutput "- 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")
hopping

assertOutputContains
(normalizeWindowsOutput "- 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")
hopping

readFileVerbatim "hops.expect.txt" >>=
flip (assertOn multilineNeedleHaystack) hopping . normalizePathSeparators

-- The project is named oops as it is like hops but has conflicting constraints.
-- +-- oops-0.project
Expand All @@ -208,36 +128,18 @@ main = cabalTest . withRepo "repo" . recordMode RecordMarked $ do
-- +-- oops/oops-9.config (has conflicting constraints)
log "checking conflicting constraints skipping into a subfolder and then back out again and again"
oopsing <- fails $ cabal' "v2-build" [ "all", "--project-file=oops-0.project" ]
assertOutputContains "rejecting: hashable-1.4.2.0" oopsing
assertOutputContains "rejecting: hashable-1.4.3.0" oopsing
assertOutputContains "(constraint from oops-0.project requires ==1.4.3.0)" oopsing

assertOutputContains
(normalizeWindowsOutput " (constraint from oops/oops-9.config requires ==1.4.2.0) \
\ imported by: oops-8.config \
\ imported by: oops/oops-7.config \
\ imported by: oops-6.config \
\ imported by: oops/oops-5.config \
\ imported by: oops-4.config \
\ imported by: oops/oops-3.config \
\ imported by: oops-2.config \
\ imported by: oops/oops-1.config \
\ imported by: oops-0.project")
oopsing

readFileVerbatim "oops.expect.txt"
>>= flip (assertOn multilineNeedleHaystack) oopsing . normalizePathSeparators

log "checking bad conditional"
badIf <- fails $ cabal' "v2-build" [ "--project-file=bad-conditional.project" ]
assertOutputContains "Cannot set compiler in a conditional clause of a cabal project file" badIf

log "checking that missing package message lists configuration provenance"
missing <- fails $ cabal' "v2-build" [ "--project-file=cabal-missing-package.project" ]
assertOutputContains
(normalizeWindowsOutput "When using configuration from: \
\ - cabal-missing-package.project \
\ - missing/pkgs.config \
\ - missing/pkgs/default.config \
\The following errors occurred: \
\ - The package location 'pkg-doesnt-exist' does not exist.")
missing

readFileVerbatim "cabal-missing-package.expect.txt"
>>= flip (assertOn multilineNeedleHaystack) missing . normalizePathSeparators

return ()
56 changes: 56 additions & 0 deletions cabal-testsuite/PackageTests/ConditionalAndImport/hops.expect.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
Configuration is affected by the following files:
- hops-0.project
- hops-2.config
imported by: hops/hops-1.config
imported by: hops-0.project
- 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
- 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
- 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
- hops/hops-1.config
imported by: hops-0.project
- hops/hops-3.config
imported by: hops-2.config
imported by: hops/hops-1.config
imported by: hops-0.project
- 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
- 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
- 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
16 changes: 16 additions & 0 deletions cabal-testsuite/PackageTests/ConditionalAndImport/oops.expect.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Could not resolve dependencies:
[__0] trying: oops-0.1 (user goal)
[__1] next goal: hashable (dependency of oops)
[__1] rejecting: hashable-1.4.3.0
(constraint from oops/oops-9.config requires ==1.4.2.0)
imported by: oops-8.config
imported by: oops/oops-7.config
imported by: oops-6.config
imported by: oops/oops-5.config
imported by: oops-4.config
imported by: oops/oops-3.config
imported by: oops-2.config
imported by: oops/oops-1.config
imported by: oops-0.project
[__1] rejecting: hashable-1.4.2.0
(constraint from oops-0.project requires ==1.4.3.0)
Loading
Loading