Cabal-3.12.0.0: src/Distribution/Simple/Glob.hs
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE LambdaCase #-}
-----------------------------------------------------------------------------
-- |
-- Module : Distribution.Simple.Glob
-- Copyright : Isaac Jones, Simon Marlow 2003-2004
-- License : BSD3
-- portions Copyright (c) 2007, Galois Inc.
--
-- Maintainer : cabal-devel@haskell.org
-- Portability : portable
--
-- Simple file globbing.
module Distribution.Simple.Glob
( -- * Globs
Glob
-- * Matching on globs
, GlobResult (..)
, globMatches
, fileGlobMatches
, matchDirFileGlob
, matchDirFileGlobWithDie
, runDirFileGlob
-- * Parsing globs
, parseFileGlob
, GlobSyntaxError (..)
, explainGlobSyntaxError
-- * Utility
, isRecursiveInRoot
)
where
import Distribution.Compat.Prelude
import Prelude ()
import Distribution.CabalSpecVersion (CabalSpecVersion)
import Distribution.Simple.Errors
( CabalException (MatchDirFileGlob, MatchDirFileGlobErrors)
)
import Distribution.Simple.Glob.Internal
import Distribution.Simple.Utils (dieWithException, warn)
import Distribution.Verbosity (Verbosity)
-------------------------------------------------------------------------------
-- * Matching
--------------------------------------------------------------------------------
-- | Extract the matches from a list of 'GlobResult's.
--
-- Note: throws away the 'GlobMissingDirectory' results; chances are
-- that you want to check for these and error out if any are present.
--
-- @since 3.12.0.0
globMatches :: [GlobResult a] -> [a]
globMatches input = [a | GlobMatch a <- input]
-- | 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.
--
-- The 'Version' argument must be the spec version of the package
-- description being processed, as globs behave slightly differently
-- in different spec versions.
--
-- The first 'FilePath' argument is the directory that the glob is
-- relative to. It must be a valid directory (and hence it can't be
-- the empty string). The returned values will not include this
-- prefix.
--
-- The second 'FilePath' is the glob itself.
matchDirFileGlob :: Verbosity -> CabalSpecVersion -> FilePath -> FilePath -> IO [FilePath]
matchDirFileGlob v = matchDirFileGlobWithDie v dieWithException
-- | Like 'matchDirFileGlob' but with customizable 'die'
--
-- @since 3.6.0.0
matchDirFileGlobWithDie
:: Verbosity
-> (Verbosity -> CabalException -> IO [FilePath])
-> CabalSpecVersion
-> FilePath
-> FilePath
-> IO [FilePath]
matchDirFileGlobWithDie verbosity rip version dir filepath = case parseFileGlob version filepath of
Left err -> rip verbosity $ MatchDirFileGlob (explainGlobSyntaxError filepath err)
Right glob -> do
results <- runDirFileGlob verbosity (Just version) dir glob
let missingDirectories =
[missingDir | GlobMissingDirectory missingDir <- results]
matches = globMatches results
directoryMatches = [a | GlobMatchesDirectory a <- results]
let errors :: [String]
errors =
[ "filepath wildcard '"
++ filepath
++ "' refers to the directory"
++ " '"
++ missingDir
++ "', which does not exist or is not a directory."
| missingDir <- missingDirectories
]
++ [ "filepath wildcard '" ++ filepath ++ "' does not match any files."
| null matches && null directoryMatches
-- we don't error out on directory matches, simply warn about them and ignore.
]
warns :: [String]
warns =
[ "Ignoring directory '" ++ path ++ "'" ++ " listed in a Cabal package field which should only include files (not directories)."
| path <- directoryMatches
]
if null errors
then do
unless (null warns) $
warn verbosity $
unlines warns
return matches
else rip verbosity $ MatchDirFileGlobErrors errors