@@ -18,7 +18,7 @@ module Distribution.Simple.Glob (
1818 GlobSyntaxError (.. ),
1919 GlobResult (.. ),
2020 matchDirFileGlob ,
21- matchDirFileGlob' ,
21+ runDirFileGlob ,
2222 fileGlobMatches ,
2323 parseFileGlob ,
2424 explainGlobSyntaxError ,
@@ -35,7 +35,7 @@ import Distribution.Verbosity
3535import Distribution.Version
3636
3737import System.Directory (getDirectoryContents , doesDirectoryExist , doesFileExist )
38- import System.FilePath (joinPath , splitExtensions , splitDirectories , takeFileName , (</>) )
38+ import System.FilePath (joinPath , splitExtensions , splitDirectories , takeFileName , (</>) , (<.>) )
3939
4040-- Note throughout that we use splitDirectories, not splitPath. On
4141-- Posix, this makes no difference, but, because Windows accepts both
@@ -127,6 +127,14 @@ data GlobFinal
127127 | FinalLit FilePath
128128 -- ^ Literal file name.
129129
130+ reconstructGlob :: Glob -> FilePath
131+ reconstructGlob (GlobStem dir glob) =
132+ dir </> reconstructGlob glob
133+ reconstructGlob (GlobFinal final) = case final of
134+ FinalMatch Recursive _ exts -> " **" </> " *" <.> exts
135+ FinalMatch NonRecursive _ exts -> " *" <.> exts
136+ FinalLit path -> path
137+
130138-- | Returns 'Nothing' if the glob didn't match at all, or 'Just' the
131139-- result if the glob matched (or would have matched with a higher
132140-- cabal-version).
@@ -199,71 +207,77 @@ parseFileGlob version filepath = case reverse (splitDirectories filepath) of
199207 | version >= mkVersion [2 ,4 ] = MultiDotEnabled
200208 | otherwise = MultiDotDisabled
201209
202- -- | Like 'matchDirFileGlob'', but will 'die'' when the glob matches
203- -- no files, or if the glob refers to a missing directory.
210+ -- | This will 'die'' when the glob matches no files, or if the glob
211+ -- refers to a missing directory, or if the glob fails to parse.
212+ --
213+ -- The returned values do not include the supplied @dir@ prefix, which
214+ -- must itself be a valid directory (hence, it can't be the empty
215+ -- string).
204216matchDirFileGlob :: Verbosity -> Version -> FilePath -> FilePath -> IO [FilePath ]
205- matchDirFileGlob verbosity version dir filepath = do
206- results <- matchDirFileGlob' verbosity version dir filepath
207- let missingDirectories =
208- [ missingDir | GlobMissingDirectory missingDir <- results ]
209- matches = globMatches results
210- -- Check for missing directories first, since we'll obviously have
211- -- no matches in that case.
212- for_ missingDirectories $ \ missingDir ->
213- die' verbosity $
214- " filepath wildcard '" ++ filepath ++ " ' refers to the directory"
215- ++ " '" ++ missingDir ++ " ', which does not exist or is not a directory."
216- when (null matches) $ die' verbosity $
217- " filepath wildcard '" ++ filepath
218- ++ " ' does not match any files."
219- return matches
217+ matchDirFileGlob verbosity version dir filepath = case parseFileGlob version filepath of
218+ Left err -> die' verbosity $ explainGlobSyntaxError filepath err
219+ Right glob -> do
220+ results <- runDirFileGlob verbosity dir glob
221+ let missingDirectories =
222+ [ missingDir | GlobMissingDirectory missingDir <- results ]
223+ matches = globMatches results
224+ -- Check for missing directories first, since we'll obviously have
225+ -- no matches in that case.
226+ for_ missingDirectories $ \ missingDir ->
227+ die' verbosity $
228+ " filepath wildcard '" ++ filepath ++ " ' refers to the directory"
229+ ++ " '" ++ missingDir ++ " ', which does not exist or is not a directory."
230+ when (null matches) $ die' verbosity $
231+ " filepath wildcard '" ++ filepath
232+ ++ " ' does not match any files."
233+ return matches
220234
221- -- | Match files against a glob, starting in a directory.
235+ -- | Match files against a pre-parsed glob, starting in a directory.
222236--
223- -- The returned values do not include the supplied @dir@ prefix.
224- matchDirFileGlob' :: Verbosity -> Version -> FilePath -> FilePath -> IO [ GlobResult FilePath ]
225- matchDirFileGlob' verbosity version rawDir filepath = case parseFileGlob version filepath of
226- Left err -> die' verbosity $ explainGlobSyntaxError filepath err
227- Right pat -> do
228- -- The default data-dir is null. Our callers -should- be
229- -- converting that to '.' themselves, but it's a certainty that
230- -- some future call-site will forget and trigger a really
231- -- hard-to-debug failure if we don't check for that here.
232- when (null rawDir) $
233- warn verbosity $
234- " Null dir passed to matchDirFileGlob ; interpreting it "
235- ++ " as '.'. This is probably an internal error."
236- let dir = if null rawDir then " ." else rawDir
237- debug verbosity $ " Expanding glob '" ++ filepath ++ " ' in directory '" ++ dir ++ " '."
238- -- This function might be called from the project root with dir as
239- -- ".". Walking the tree starting there involves going into .git/
240- -- and dist-newstyle/, which is a lot of work for no reward, so
241- -- extract the constant prefix from the pattern and start walking
242- -- there, and only walk as much as we need to: recursively if **,
243- -- the whole directory if *, and just the specific file if it's a
244- -- literal.
245- let (prefixSegments, final) = splitConstantPrefix pat
246- joinedPrefix = joinPath prefixSegments
247- case final of
248- FinalMatch recursive multidot exts -> do
249- let prefix = dir </> joinedPrefix
250- directoryExists <- doesDirectoryExist prefix
251- if directoryExists
252- then do
253- candidates <- case recursive of
254- Recursive -> getDirectoryContentsRecursive prefix
255- NonRecursive -> filterM (doesFileExist . (prefix </> )) =<< getDirectoryContents prefix
256- let checkName candidate = do
257- let (candidateBase, candidateExts) = splitExtensions $ takeFileName candidate
258- guard (not (null candidateBase))
259- match <- checkExt multidot exts candidateExts
260- return (joinedPrefix </> candidate <$ match)
261- return $ mapMaybe checkName candidates
262- else
263- return [ GlobMissingDirectory joinedPrefix ]
264- FinalLit fn -> do
265- exists <- doesFileExist (dir </> joinedPrefix </> fn)
266- return [ GlobMatch (joinedPrefix </> fn) | exists ]
237+ -- The returned values do not include the supplied @dir@ prefix, which
238+ -- must itself be a valid directory (hence, it can't be the empty
239+ -- string).
240+ runDirFileGlob :: Verbosity -> FilePath -> Glob -> IO [ GlobResult FilePath ]
241+ runDirFileGlob verbosity rawDir pat = do
242+ -- The default data-dir is null. Our callers -should- be
243+ -- converting that to '.' themselves, but it's a certainty that
244+ -- some future call-site will forget and trigger a really
245+ -- hard-to-debug failure if we don't check for that here.
246+ when (null rawDir) $
247+ warn verbosity $
248+ " Null dir passed to runDirFileGlob ; interpreting it "
249+ ++ " as '.'. This is probably an internal error."
250+ let dir = if null rawDir then " ." else rawDir
251+ debug verbosity $ " Expanding glob '" ++ reconstructGlob pat ++ " ' in directory '" ++ dir ++ " '."
252+ -- This function might be called from the project root with dir as
253+ -- ".". Walking the tree starting there involves going into .git/
254+ -- and dist-newstyle/, which is a lot of work for no reward, so
255+ -- extract the constant prefix from the pattern and start walking
256+ -- there, and only walk as much as we need to: recursively if **,
257+ -- the whole directory if *, and just the specific file if it's a
258+ -- literal.
259+ let (prefixSegments, final) = splitConstantPrefix pat
260+ joinedPrefix = joinPath prefixSegments
261+ case final of
262+ FinalMatch recursive multidot exts -> do
263+ let prefix = dir </> joinedPrefix
264+ directoryExists <- doesDirectoryExist prefix
265+ if directoryExists
266+ then do
267+ candidates <- case recursive of
268+ Recursive -> getDirectoryContentsRecursive prefix
269+ NonRecursive -> filterM (doesFileExist . (prefix </> )) =<< getDirectoryContents prefix
270+ let checkName candidate = do
271+ let (candidateBase, candidateExts) = splitExtensions $ takeFileName candidate
272+ guard (not (null candidateBase))
273+ match <- checkExt multidot exts candidateExts
274+ return (joinedPrefix </> candidate <$ match)
275+ return $ mapMaybe checkName candidates
276+ else
277+ return [ GlobMissingDirectory joinedPrefix ]
278+ FinalLit fn -> do
279+ exists <- doesFileExist (dir </> joinedPrefix </> fn)
280+ return [ GlobMatch (joinedPrefix </> fn) | exists ]
267281
268282unfoldr' :: (a -> Either r (b , a )) -> a -> ([b ], r )
269283unfoldr' f a = case f a of
0 commit comments