Cabal-3.18.1.0: src/Distribution/Verbosity.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeApplications #-}
-----------------------------------------------------------------------------
-- Verbosity for Cabal functions.
-- |
-- Module : Distribution.Verbosity
-- Copyright : Ian Lynagh 2007
-- License : BSD3
--
-- Maintainer : cabal-devel@haskell.org
-- Portability : portable
--
-- A 'Verbosity' type with associated utilities.
--
-- There are 4 standard verbosity levels from 'silent', 'normal',
-- 'verbose' up to 'deafening'. This is used for deciding what logging
-- messages to print.
--
-- Verbosity also is equipped with some internal settings which can be
-- used to control at a fine granularity the verbosity of specific
-- settings (e.g., so that you can trace only particular things you
-- are interested in.) It's important to note that the instances
-- for 'Verbosity' assume that this does not exist.
module Distribution.Verbosity
( -- * Rich verbosity
Verbosity (..)
, VerbosityHandles (..)
, defaultVerbosityHandles
, VerbosityLevel (..)
, verbosityLevel
, verbosityChosenOutputHandle
, verbosityErrorHandle
, modifyVerbosityFlags
, mkVerbosity
, setVerbosityHandles
-- * Verbosity flags
, VerbosityFlags (vLevel)
, mkVerbosityFlags
, makeVerbose
, silent
, normal
, verbose
, deafening
, moreVerbose
, lessVerbose
, isVerboseQuiet
, intToVerbosity
, flagToVerbosity
, showForCabal
, showForGHC
, verboseNoFlags
, verboseHasFlags
-- * Call stacks
, verboseCallSite
, verboseCallStack
, isVerboseCallSite
, isVerboseCallStack
-- * Output markers
, verboseMarkOutput
, isVerboseMarkOutput
, verboseUnmarkOutput
-- * Line wrapping
, verboseNoWrap
, isVerboseNoWrap
-- * Time stamps
, verboseTimestamp
, isVerboseTimestamp
, verboseNoTimestamp
-- * Stderr
, verboseStderr
, isVerboseStderr
, verboseNoStderr
-- * No warnings
, verboseNoWarn
, isVerboseNoWarn
) where
import Distribution.Compat.Prelude
import Prelude ()
import Distribution.ReadE
import Data.List (elemIndex)
import Distribution.Parsec
import Distribution.Pretty
import Distribution.Utils.Generic (isAsciiAlpha)
import Distribution.Verbosity.Internal
import qualified Data.Set as Set
import qualified Distribution.Compat.CharParsing as P
import Distribution.Utils.Structured
import System.IO (Handle, stderr, stdout)
import qualified Text.PrettyPrint as PP
import qualified Type.Reflection as Typeable
-- | Rich verbosity, used for the Cabal library interface.
data Verbosity = Verbosity
{ verbosityFlags :: VerbosityFlags
, verbosityHandles :: VerbosityHandles
}
deriving (Generic)
-- | Handles to use for logging (e.g. log to stdout, or log to a file).
data VerbosityHandles = VerbosityHandles
{ vStdoutHandle :: Handle
, vStderrHandle :: Handle
}
defaultVerbosityHandles :: VerbosityHandles
defaultVerbosityHandles =
VerbosityHandles
{ vStdoutHandle = stdout
, vStderrHandle = stderr
}
-- | Verbosity information which can be passed by the CLI.
data VerbosityFlags = VerbosityFlags
{ vLevel :: VerbosityLevel
, vFlags :: Set VerbosityFlag
, vQuiet :: Bool
}
deriving (Generic, Show, Read, Eq)
verbosityLevel :: Verbosity -> VerbosityLevel
verbosityLevel = vLevel . verbosityFlags
-- | The handle used for normal output.
--
-- With the @+stderr@ verbosity flag, this is the error handle.
verbosityChosenOutputHandle :: Verbosity -> Handle
verbosityChosenOutputHandle verb =
if isVerboseStderr (verbosityFlags verb)
then vStderrHandle $ verbosityHandles verb
else vStdoutHandle $ verbosityHandles verb
-- | The verbosity handle used for error output.
verbosityErrorHandle :: Verbosity -> Handle
verbosityErrorHandle = vStderrHandle . verbosityHandles
setVerbosityHandles :: Maybe Handle -> Verbosity -> Verbosity
setVerbosityHandles Nothing v = v
setVerbosityHandles (Just h) v =
v{verbosityHandles = VerbosityHandles{vStdoutHandle = h, vStderrHandle = h}}
mkVerbosity :: VerbosityHandles -> VerbosityFlags -> Verbosity
mkVerbosity handles flags =
Verbosity
{ verbosityFlags = flags
, verbosityHandles = handles
}
modifyVerbosityFlags :: (VerbosityFlags -> VerbosityFlags) -> Verbosity -> Verbosity
modifyVerbosityFlags f v@(Verbosity{verbosityFlags = flags}) =
v{verbosityFlags = f flags}
mkVerbosityFlags :: VerbosityLevel -> VerbosityFlags
mkVerbosityFlags l = VerbosityFlags{vLevel = l, vFlags = Set.empty, vQuiet = False}
instance Binary VerbosityFlags
instance NFData VerbosityFlags
instance Structured VerbosityFlags
-- Hand-written instances, because there are no NFData/Structured instances
-- for Handle.
instance NFData VerbosityHandles where
rnf (VerbosityHandles o e) = o `seq` e `seq` ()
instance Structured VerbosityHandles where
structure _ =
Structure
tr
0
(show tr)
[
( "VerbosityHandles"
,
[ nominalStructure $ Proxy @Handle
, nominalStructure $ Proxy @Handle
]
)
]
where
tr = Typeable.SomeTypeRep $ Typeable.typeRep @VerbosityHandles
instance NFData Verbosity
instance Structured Verbosity
-- | In 'silent' mode, we should not print /anything/ unless an error occurs.
silent :: VerbosityFlags
silent = mkVerbosityFlags Silent
-- | Print stuff we want to see by default.
normal :: VerbosityFlags
normal = mkVerbosityFlags Normal
-- | Be more verbose about what's going on.
verbose :: VerbosityFlags
verbose = mkVerbosityFlags Verbose
-- | Not only are we verbose ourselves (perhaps even noisier than when
-- being 'verbose'), but we tell everything we run to be verbose too.
deafening :: VerbosityFlags
deafening = mkVerbosityFlags Deafening
-- | Increase verbosity level, but stay 'silent' if we are.
moreVerbose :: VerbosityFlags -> VerbosityFlags
moreVerbose v =
case vLevel v of
Silent -> v -- silent should stay silent
Normal -> v{vLevel = Verbose}
Verbose -> v{vLevel = Deafening}
Deafening -> v
-- | Make sure the verbosity level is at least 'verbose',
-- but stay 'silent' if we are.
makeVerbose :: VerbosityFlags -> VerbosityFlags
makeVerbose v =
case vLevel v of
Silent -> v -- silent should stay silent
Normal -> v{vLevel = Verbose}
Verbose -> v
Deafening -> v
-- | Decrease verbosity level, but stay 'deafening' if we are.
lessVerbose :: VerbosityFlags -> VerbosityFlags
lessVerbose v =
verboseQuiet $
case vLevel v of
Deafening -> v -- deafening stays deafening
Verbose -> v{vLevel = Normal}
Normal -> v{vLevel = Silent}
Silent -> v
-- | Numeric verbosity level @0..3@: @0@ is 'silent', @3@ is 'deafening'.
intToVerbosity :: Int -> Maybe VerbosityFlags
intToVerbosity 0 = Just (mkVerbosityFlags Silent)
intToVerbosity 1 = Just (mkVerbosityFlags Normal)
intToVerbosity 2 = Just (mkVerbosityFlags Verbose)
intToVerbosity 3 = Just (mkVerbosityFlags Deafening)
intToVerbosity _ = Nothing
-- | Parser verbosity
--
-- >>> explicitEitherParsec parsecVerbosity "normal"
-- Right (VerbosityFlags {vLevel = Normal, vFlags = fromList [], vQuiet = False})
--
-- >>> explicitEitherParsec parsecVerbosity "normal+nowrap "
-- Right (VerbosityFlags {vLevel = Normal, vFlags = fromList [VNoWrap], vQuiet = False})
--
-- >>> explicitEitherParsec parsecVerbosity "normal+nowrap +markoutput"
-- Right (VerbosityFlags {vLevel = Normal, vFlags = fromList [VNoWrap,VMarkOutput], vQuiet = False})
--
-- >>> explicitEitherParsec parsecVerbosity "normal +nowrap +markoutput"
-- Right (VerbosityFlags {vLevel = Normal, vFlags = fromList [VNoWrap,VMarkOutput], vQuiet = False})
--
-- >>> explicitEitherParsec parsecVerbosity "normal+nowrap+markoutput"
-- Right (VerbosityFlags {vLevel = Normal, vFlags = fromList [VNoWrap,VMarkOutput], vQuiet = False})
--
-- >>> explicitEitherParsec parsecVerbosity "deafening+nowrap+stdout+stderr+callsite+callstack"
-- Right (VerbosityFlags {vLevel = Deafening, vFlags = fromList [VCallStack,VCallSite,VNoWrap,VStderr], vQuiet = False})
--
-- /Note:/ this parser will eat trailing spaces.
instance Parsec VerbosityFlags where
parsec = parsecVerbosity
instance Pretty VerbosityFlags where
pretty = PP.text . showForCabal
parsecVerbosity :: CabalParsing m => m VerbosityFlags
parsecVerbosity = parseIntVerbosity <|> parseStringVerbosity
where
parseIntVerbosity = do
i <- P.integral
case intToVerbosity i of
Just v -> return v
Nothing -> P.unexpected $ "Bad integral verbosity: " ++ show i ++ ". Valid values are 0..3"
parseStringVerbosity = do
level <- parseVerbosityLevel
_ <- P.spaces
flags <- many (parseFlag <* P.spaces)
return $ foldl' (flip ($)) (mkVerbosityFlags level) flags
parseVerbosityLevel = do
token <- P.munch1 isAsciiAlpha
case token of
"silent" -> return Silent
"normal" -> return Normal
"verbose" -> return Verbose
"debug" -> return Deafening
"deafening" -> return Deafening
_ -> P.unexpected $ "Bad verbosity level: " ++ token
parseFlag = do
_ <- P.char '+'
token <- P.munch1 isAsciiAlpha
case token of
"callsite" -> return verboseCallSite
"callstack" -> return verboseCallStack
"nowrap" -> return verboseNoWrap
"markoutput" -> return verboseMarkOutput
"timestamp" -> return verboseTimestamp
"stderr" -> return verboseStderr
"stdout" -> return verboseNoStderr
"nowarn" -> return verboseNoWarn
_ -> P.unexpected $ "Bad verbosity flag: " ++ token
flagToVerbosity :: ReadE VerbosityFlags
flagToVerbosity = parsecToReadE id parsecVerbosity
showForCabal :: VerbosityFlags -> String
showForCabal (VerbosityFlags{vLevel = lvl, vFlags = flags})
| Set.null flags =
maybe (error "unknown verbosity") show $
elemIndex lvl [Silent, Normal, Verbose, Deafening]
| otherwise =
unwords $
showLevel lvl
: concatMap showFlag (Set.toList flags)
where
showLevel Silent = "silent"
showLevel Normal = "normal"
showLevel Verbose = "verbose"
showLevel Deafening = "debug"
showFlag VCallSite = ["+callsite"]
showFlag VCallStack = ["+callstack"]
showFlag VNoWrap = ["+nowrap"]
showFlag VMarkOutput = ["+markoutput"]
showFlag VTimestamp = ["+timestamp"]
showFlag VStderr = ["+stderr"]
showFlag VNoWarn = ["+nowarn"]
showForGHC :: VerbosityFlags -> String
showForGHC v =
maybe (error "unknown verbosity") show $
elemIndex (vLevel v) [Silent, Normal, __, Verbose, Deafening]
where
__ = Silent -- this will be always ignored by elemIndex
-- | Turn on verbose call-site printing when we log.
verboseCallSite :: VerbosityFlags -> VerbosityFlags
verboseCallSite = verboseFlag VCallSite
-- | Turn on verbose call-stack printing when we log.
verboseCallStack :: VerbosityFlags -> VerbosityFlags
verboseCallStack = verboseFlag VCallStack
-- | Turn on @-----BEGIN CABAL OUTPUT-----@ markers for output
-- from Cabal (as opposed to GHC, or system dependent).
verboseMarkOutput :: VerbosityFlags -> VerbosityFlags
verboseMarkOutput = verboseFlag VMarkOutput
-- | Turn off marking; useful for suppressing nondeterministic output.
verboseUnmarkOutput :: VerbosityFlags -> VerbosityFlags
verboseUnmarkOutput = verboseNoFlag VMarkOutput
-- | Disable line-wrapping for log messages.
verboseNoWrap :: VerbosityFlags -> VerbosityFlags
verboseNoWrap = verboseFlag VNoWrap
-- | Mark the verbosity as quiet.
verboseQuiet :: VerbosityFlags -> VerbosityFlags
verboseQuiet v = v{vQuiet = True}
-- | Turn on timestamps for log messages.
verboseTimestamp :: VerbosityFlags -> VerbosityFlags
verboseTimestamp = verboseFlag VTimestamp
-- | Turn off timestamps for log messages.
verboseNoTimestamp :: VerbosityFlags -> VerbosityFlags
verboseNoTimestamp = verboseNoFlag VTimestamp
-- | Switch logging to 'stderr'.
--
-- @since 3.4.0.0
verboseStderr :: VerbosityFlags -> VerbosityFlags
verboseStderr = verboseFlag VStderr
-- | Switch logging to 'stdout'.
--
-- @since 3.4.0.0
verboseNoStderr :: VerbosityFlags -> VerbosityFlags
verboseNoStderr = verboseNoFlag VStderr
-- | Turn off warnings for log messages.
verboseNoWarn :: VerbosityFlags -> VerbosityFlags
verboseNoWarn = verboseFlag VNoWarn
-- | Helper function for flag enabling functions.
verboseFlag :: VerbosityFlag -> (VerbosityFlags -> VerbosityFlags)
verboseFlag flag v@(VerbosityFlags{vFlags = flags}) = v{vFlags = Set.insert flag flags}
-- | Helper function for flag disabling functions.
verboseNoFlag :: VerbosityFlag -> (VerbosityFlags -> VerbosityFlags)
verboseNoFlag flag v@(VerbosityFlags{vFlags = flags}) = v{vFlags = Set.delete flag flags}
-- | Turn off all flags.
verboseNoFlags :: VerbosityFlags -> VerbosityFlags
verboseNoFlags v = v{vFlags = Set.empty}
verboseHasFlags :: VerbosityFlags -> Bool
verboseHasFlags (VerbosityFlags{vFlags = flags}) = not $ Set.null flags
-- | Test if we should output call sites when we log.
isVerboseCallSite :: VerbosityFlags -> Bool
isVerboseCallSite = isVerboseFlag VCallSite
-- | Test if we should output call stacks when we log.
isVerboseCallStack :: VerbosityFlags -> Bool
isVerboseCallStack = isVerboseFlag VCallStack
-- | Test if we should output markers.
isVerboseMarkOutput :: VerbosityFlags -> Bool
isVerboseMarkOutput = isVerboseFlag VMarkOutput
-- | Test if line-wrapping is disabled for log messages.
isVerboseNoWrap :: VerbosityFlags -> Bool
isVerboseNoWrap = isVerboseFlag VNoWrap
-- | Test if we had called 'lessVerbose' on the verbosity.
isVerboseQuiet :: VerbosityFlags -> Bool
isVerboseQuiet = vQuiet
-- | Test if we should output timestamps when we log.
isVerboseTimestamp :: VerbosityFlags -> Bool
isVerboseTimestamp = isVerboseFlag VTimestamp
-- | Test if we should output to 'stderr' when we log.
--
-- @since 3.4.0.0
isVerboseStderr :: VerbosityFlags -> Bool
isVerboseStderr = isVerboseFlag VStderr
-- | Test if we should output warnings when we log.
isVerboseNoWarn :: VerbosityFlags -> Bool
isVerboseNoWarn = isVerboseFlag VNoWarn
-- | Helper function for flag testing functions.
isVerboseFlag :: VerbosityFlag -> VerbosityFlags -> Bool
isVerboseFlag flag v = flag `Set.member` vFlags v
-- $setup
-- >>> import Test.QuickCheck (Arbitrary (..), arbitraryBoundedEnum)
-- >>> instance Arbitrary VerbosityLevel where arbitrary = arbitraryBoundedEnum
-- >>> instance Arbitrary VerbosityFlags where arbitrary = fmap mkVerbosityFlags arbitrary