Skip to content

Commit

Permalink
allow glob matches of the form dir/**/FileNoExtension (#8005)
Browse files Browse the repository at this point in the history
* allow glob matches of the form dir/**/FileNoExtension

* allow glob matches of the form dir/**/FileNoExtension

* tests, docs, changelong

Co-authored-by: Gershom Bazerman <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Apr 13, 2022
1 parent 9ca9891 commit f60c9d7
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 16 deletions.
2 changes: 2 additions & 0 deletions Cabal-tests/tests/CheckTests.hs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ checkTests = testGroup "regressions"
, checkTest "pre-1.6-glob.cabal"
, checkTest "pre-2.4-globstar.cabal"
, checkTest "bad-glob-syntax.cabal"
, checkTest "globstar-literal.cabal"
, checkTest "pre-3.8-globstar-literal.cabal"
, checkTest "cc-options-with-optimization.cabal"
, checkTest "cxx-options-with-optimization.cabal"
, checkTest "ghc-option-j.cabal"
Expand Down
16 changes: 16 additions & 0 deletions Cabal-tests/tests/ParserTests/regressions/globstar-literal.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cabal-version: 3.8
name: globstar-literal
version: 0
extra-source-files:
foo/**/bar

license: BSD-3-Clause
synopsis: no
description: none
category: Test
maintainer: none

library
default-language: Haskell2010
exposed-modules:
Foo
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cabal-version: 3.6
name: globstar-literal
version: 0
extra-source-files:
foo/**/bar

license: BSD-3-Clause
synopsis: no
description: none
category: Test
maintainer: none

library
default-language: Haskell2010
exposed-modules:
Foo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
In the 'extra-source-files' field: invalid file glob 'foo/**/bar'. Prior to 'cabal-version: 3.8' if a wildcard '**' is used as a parent directory, the file's base name must be a wildcard '*'.
42 changes: 30 additions & 12 deletions Cabal/src/Distribution/Simple/Glob.hs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ explainGlobSyntaxError filepath NoExtensionOnStar =
++ "'. If a wildcard '*' is used it must be with an file extension."
explainGlobSyntaxError filepath LiteralFileNameGlobStar =
"invalid file glob '" ++ filepath
++ "'. If a wildcard '**' is used as a parent directory, the"
++ "'. Prior to 'cabal-version: 3.8'"
++ " if a wildcard '**' is used as a parent directory, the"
++ " file's base name must be a wildcard '*'."
explainGlobSyntaxError _ EmptyGlob =
"invalid file glob. A glob cannot be the empty string."
Expand All @@ -111,7 +112,7 @@ explainGlobSyntaxError filepath VersionDoesNotSupportGlob =
++ "Alternatively if you require compatibility with earlier Cabal "
++ "versions then list all the files explicitly."

data IsRecursive = Recursive | NonRecursive
data IsRecursive = Recursive | NonRecursive deriving Eq

data MultiDot = MultiDotDisabled | MultiDotEnabled

Expand All @@ -125,7 +126,7 @@ data GlobFinal
-- ^ First argument: Is this a @**/*.ext@ pattern?
-- Second argument: should we match against the exact extensions, or accept a suffix?
-- Third argument: the extensions to accept.
| FinalLit FilePath
| FinalLit IsRecursive FilePath
-- ^ Literal file name.

reconstructGlob :: Glob -> FilePath
Expand All @@ -134,7 +135,8 @@ reconstructGlob (GlobStem dir glob) =
reconstructGlob (GlobFinal final) = case final of
FinalMatch Recursive _ exts -> "**" </> "*" <.> exts
FinalMatch NonRecursive _ exts -> "*" <.> exts
FinalLit path -> path
FinalLit Recursive path -> "**" </> path
FinalLit NonRecursive path -> path

-- | Returns 'Nothing' if the glob didn't match at all, or 'Just' the
-- result if the glob matched (or would have matched with a higher
Expand All @@ -159,8 +161,8 @@ fileGlobMatchesSegments pat (seg : segs) = case pat of
let (candidateBase, candidateExts) = splitExtensions seg
guard (null segs && not (null candidateBase))
checkExt multidot ext candidateExts
FinalLit filename -> do
guard (null segs && filename == seg)
FinalLit isRecursive filename -> do
guard ((isRecursive == Recursive || null segs) && filename == seg)
return (GlobMatch ())

checkExt
Expand All @@ -181,12 +183,14 @@ parseFileGlob version filepath = case reverse (splitDirectories filepath) of
Left EmptyGlob
(filename : "**" : segments)
| allowGlobStar -> do
ext <- case splitExtensions filename of
finalSegment <- case splitExtensions filename of
("*", ext) | '*' `elem` ext -> Left StarInExtension
| null ext -> Left NoExtensionOnStar
| otherwise -> Right ext
_ -> Left LiteralFileNameGlobStar
foldM addStem (GlobFinal $ FinalMatch Recursive multidot ext) segments
| otherwise -> Right (FinalMatch Recursive multidot ext)
_ -> if allowLiteralFilenameGlobStar
then Right (FinalLit Recursive filename)
else Left LiteralFileNameGlobStar
foldM addStem (GlobFinal finalSegment) segments
| otherwise -> Left VersionDoesNotSupportGlobStar
(filename : segments) -> do
pat <- case splitExtensions filename of
Expand All @@ -196,7 +200,7 @@ parseFileGlob version filepath = case reverse (splitDirectories filepath) of
| otherwise -> Right (FinalMatch NonRecursive multidot ext)
(_, ext) | '*' `elem` ext -> Left StarInExtension
| '*' `elem` filename -> Left StarInFileName
| otherwise -> Right (FinalLit filename)
| otherwise -> Right (FinalLit NonRecursive filename)
foldM addStem (GlobFinal pat) segments
where
allowGlob = version >= CabalSpecV1_6
Expand All @@ -207,6 +211,7 @@ parseFileGlob version filepath = case reverse (splitDirectories filepath) of
multidot
| version >= CabalSpecV2_4 = MultiDotEnabled
| otherwise = MultiDotDisabled
allowLiteralFilenameGlobStar = version >= CabalSpecV3_8

-- | This will 'die'' when the glob matches no files, or if the glob
-- refers to a missing directory, or if the glob fails to parse.
Expand Down Expand Up @@ -300,7 +305,20 @@ runDirFileGlob verbosity rawDir pat = do
return $ mapMaybe checkName candidates
else
return [ GlobMissingDirectory joinedPrefix ]
FinalLit fn -> do
FinalLit Recursive fn -> do
let prefix = dir </> joinedPrefix
directoryExists <- doesDirectoryExist prefix
if directoryExists
then do
candidates <- getDirectoryContentsRecursive prefix
let checkName candidate
| takeFileName candidate == fn = Just $ GlobMatch (joinedPrefix </> candidate)
| otherwise = Nothing
return $ mapMaybe checkName candidates
else
return [ GlobMissingDirectory joinedPrefix ]

FinalLit NonRecursive fn -> do
exists <- doesFileExist (dir </> joinedPrefix </> fn)
return [ GlobMatch (joinedPrefix </> fn) | exists ]

Expand Down
9 changes: 9 additions & 0 deletions changelog.d/pr-8005
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
synopsis: Allow glob-star matches with literal filenames (no extensions)
packages: Cabal
prs: #8005
issues: #5883
description: {

- Cabal file glob syntax extended to allow matches of the form dir/**/FileNoExtension

}
9 changes: 6 additions & 3 deletions doc/cabal-package.rst
Original file line number Diff line number Diff line change
Expand Up @@ -742,10 +742,13 @@ describe the package as a whole:

- ``**`` wildcards can only appear as the final path component
before the file name (e.g., ``data/**/images/*.jpg`` is not
allowed). If a ``**`` wildcard is used, then the file name must
include a ``*`` wildcard (e.g., ``data/**/README.rst`` is not
allowed).

- Prior to Cabal 3.8, if a ``**`` wildcard is used, then
the file name must include a ``*`` wildcard (e.g.,
``data/**/README.rst`` was not allowed). As of ``cabal-version:
3.8`` or greater, this restriction is lifted.

- A wildcard that does not match any files is an error.

The reason for providing only a very limited form of wildcard is to
Expand Down Expand Up @@ -2319,7 +2322,7 @@ system-dependent values for these fields.
Cabal files with :pkg-field:`cabal-version` < 3.0 suffer from an
infelicity in how the entries of :pkg-field:`mixins` are parsed: an
entry will fail to parse if the provided renaming clause has whitespace
after the opening parenthesis.
after the opening parenthesis.

See issues :issue:`5150`, :issue:`4864`, and :issue:`5293`.

Expand Down
7 changes: 6 additions & 1 deletion doc/file-format-changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ relative to the respective preceding *published* version.
versions of the ``Cabal`` library denote unreleased development
branches which have no stability guarantee.

``cabal-version: 3.x``
``cabal-version: 3.8``
----------------------

* Added field ``code-generators`` to :pkg-section:`test-suite` stanzas. This
Expand All @@ -45,6 +45,11 @@ relative to the respective preceding *published* version.
When :pkg-field:`extra-lib-dirs-static` is not given, it defaults to
:pkg-field:`extra-lib-dirs`.

* Wildcard matching has been slightly expanded. Matches are now
allowed of the form ``foo/**/literalFile``. Prior, double-star
wildcards required the trailing filename itself be a wildcard.


``cabal-version: 3.6``
----------------------

Expand Down

0 comments on commit f60c9d7

Please sign in to comment.