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

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

Merged
merged 6 commits into from
Apr 13, 2022
Merged
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
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