module Distribution.PackageDescription.PrettyPrint (
writeGenericPackageDescription,
showGenericPackageDescription,
writePackageDescription,
showPackageDescription,
writeHookedBuildInfo,
showHookedBuildInfo,
) where
import Prelude ()
import Distribution.Compat.Prelude
import Distribution.Types.Dependency
import Distribution.Types.ForeignLib (ForeignLib (foreignLibName))
import Distribution.Types.UnqualComponentName
import Distribution.Types.CondTree
import Distribution.PackageDescription
import Distribution.Simple.Utils
import Distribution.ParseUtils
import Distribution.Text
import Distribution.FieldGrammar (PrettyFieldGrammar', prettyFieldGrammar)
import Distribution.PackageDescription.FieldGrammar
(packageDescriptionFieldGrammar, buildInfoFieldGrammar,
flagFieldGrammar, foreignLibFieldGrammar, libraryFieldGrammar,
benchmarkFieldGrammar, testSuiteFieldGrammar,
setupBInfoFieldGrammar, sourceRepoFieldGrammar, executableFieldGrammar)
import qualified Distribution.PackageDescription.FieldGrammar as FG
import Text.PrettyPrint
(hsep, space, parens, char, nest, ($$), (<+>),
text, vcat, ($+$), Doc, render)
import qualified Data.ByteString.Lazy.Char8 as BS.Char8
writeGenericPackageDescription :: FilePath -> GenericPackageDescription -> NoCallStackIO ()
writeGenericPackageDescription fpath pkg = writeUTF8File fpath (showGenericPackageDescription pkg)
showGenericPackageDescription :: GenericPackageDescription -> String
showGenericPackageDescription = render . ppGenericPackageDescription
ppGenericPackageDescription :: GenericPackageDescription -> Doc
ppGenericPackageDescription gpd =
ppPackageDescription (packageDescription gpd)
$+$ ppSetupBInfo (setupBuildInfo (packageDescription gpd))
$+$ ppGenPackageFlags (genPackageFlags gpd)
$+$ ppCondLibrary (condLibrary gpd)
$+$ ppCondSubLibraries (condSubLibraries gpd)
$+$ ppCondForeignLibs (condForeignLibs gpd)
$+$ ppCondExecutables (condExecutables gpd)
$+$ ppCondTestSuites (condTestSuites gpd)
$+$ ppCondBenchmarks (condBenchmarks gpd)
ppPackageDescription :: PackageDescription -> Doc
ppPackageDescription pd =
prettyFieldGrammar packageDescriptionFieldGrammar pd
$+$ ppSourceRepos (sourceRepos pd)
ppSourceRepos :: [SourceRepo] -> Doc
ppSourceRepos [] = mempty
ppSourceRepos (hd:tl) = ppSourceRepo hd $+$ ppSourceRepos tl
ppSourceRepo :: SourceRepo -> Doc
ppSourceRepo repo =
emptyLine $ text "source-repository" <+> disp kind $+$
nest indentWith (prettyFieldGrammar (sourceRepoFieldGrammar kind) repo)
where
kind = repoKind repo
ppSetupBInfo :: Maybe SetupBuildInfo -> Doc
ppSetupBInfo Nothing = mempty
ppSetupBInfo (Just sbi)
| defaultSetupDepends sbi = mempty
| otherwise =
emptyLine $ text "custom-setup" $+$
nest indentWith (prettyFieldGrammar (setupBInfoFieldGrammar False) sbi)
ppGenPackageFlags :: [Flag] -> Doc
ppGenPackageFlags flds = vcat [ppFlag f | f <- flds]
ppFlag :: Flag -> Doc
ppFlag flag@(MkFlag name _ _ _) =
emptyLine $ text "flag" <+> ppFlagName name $+$
nest indentWith (prettyFieldGrammar (flagFieldGrammar name) flag)
ppCondTree2 :: PrettyFieldGrammar' s -> CondTree ConfVar [Dependency] s -> Doc
ppCondTree2 grammar = go
where
go (CondNode it _ ifs) =
prettyFieldGrammar grammar it
$+$ vcat (map ppIf ifs)
ppIf (CondBranch c thenTree Nothing)
| otherwise = ppIfCondition c $$ nest indentWith thenDoc
where
thenDoc = go thenTree
ppIf (CondBranch c thenTree (Just elseTree)) =
case (False, False) of
(True, True) -> mempty
(False, True) -> ppIfCondition c $$ nest indentWith thenDoc
(True, False) -> ppIfCondition (cNot c) $$ nest indentWith elseDoc
(False, False) -> (ppIfCondition c $$ nest indentWith thenDoc)
$+$ (text "else" $$ nest indentWith elseDoc)
where
thenDoc = go thenTree
elseDoc = go elseTree
ppCondLibrary :: Maybe (CondTree ConfVar [Dependency] Library) -> Doc
ppCondLibrary Nothing = mempty
ppCondLibrary (Just condTree) =
emptyLine $ text "library" $+$
nest indentWith (ppCondTree2 (libraryFieldGrammar Nothing) condTree)
ppCondSubLibraries :: [(UnqualComponentName, CondTree ConfVar [Dependency] Library)] -> Doc
ppCondSubLibraries libs = vcat
[ emptyLine $ (text "library" <+> disp n) $+$
nest indentWith (ppCondTree2 (libraryFieldGrammar $ Just n) condTree)
| (n, condTree) <- libs
]
ppCondForeignLibs :: [(UnqualComponentName, CondTree ConfVar [Dependency] ForeignLib)] -> Doc
ppCondForeignLibs flibs = vcat
[ emptyLine $ (text "library" <+> disp n) $+$
nest indentWith (ppCondTree2 (foreignLibFieldGrammar n) condTree)
| (n, condTree) <- flibs
]
ppCondExecutables :: [(UnqualComponentName, CondTree ConfVar [Dependency] Executable)] -> Doc
ppCondExecutables exes = vcat
[ emptyLine $ (text "executable" <+> disp n) $+$
nest indentWith (ppCondTree2 (executableFieldGrammar n) condTree)
| (n, condTree) <- exes
]
ppCondTestSuites :: [(UnqualComponentName, CondTree ConfVar [Dependency] TestSuite)] -> Doc
ppCondTestSuites suites = vcat
[ emptyLine $ (text "test-suite" <+> disp n) $+$
nest indentWith (ppCondTree2 testSuiteFieldGrammar (fmap FG.unvalidateTestSuite condTree))
| (n, condTree) <- suites
]
ppCondBenchmarks :: [(UnqualComponentName, CondTree ConfVar [Dependency] Benchmark)] -> Doc
ppCondBenchmarks suites = vcat
[ emptyLine $ (text "benchmark" <+> disp n) $+$
nest indentWith (ppCondTree2 benchmarkFieldGrammar (fmap FG.unvalidateBenchmark condTree))
| (n, condTree) <- suites
]
ppCondition :: Condition ConfVar -> Doc
ppCondition (Var x) = ppConfVar x
ppCondition (Lit b) = text (show b)
ppCondition (CNot c) = char '!' <<>> (ppCondition c)
ppCondition (COr c1 c2) = parens (hsep [ppCondition c1, text "||"
<+> ppCondition c2])
ppCondition (CAnd c1 c2) = parens (hsep [ppCondition c1, text "&&"
<+> ppCondition c2])
ppConfVar :: ConfVar -> Doc
ppConfVar (OS os) = text "os" <<>> parens (disp os)
ppConfVar (Arch arch) = text "arch" <<>> parens (disp arch)
ppConfVar (Flag name) = text "flag" <<>> parens (ppFlagName name)
ppConfVar (Impl c v) = text "impl" <<>> parens (disp c <+> disp v)
ppFlagName :: FlagName -> Doc
ppFlagName = text . unFlagName
ppIfCondition :: (Condition ConfVar) -> Doc
ppIfCondition c = (emptyLine $ text "if" <+> ppCondition c)
emptyLine :: Doc -> Doc
emptyLine d = text "" $+$ d
writePackageDescription :: FilePath -> PackageDescription -> NoCallStackIO ()
writePackageDescription fpath pkg = writeUTF8File fpath (showPackageDescription pkg)
showPackageDescription :: PackageDescription -> String
showPackageDescription = showGenericPackageDescription . pdToGpd
pdToGpd :: PackageDescription -> GenericPackageDescription
pdToGpd pd = GenericPackageDescription
{ packageDescription = pd
, genPackageFlags = []
, condLibrary = mkCondTree <$> library pd
, condSubLibraries = mkCondTreeL <$> subLibraries pd
, condForeignLibs = mkCondTree' foreignLibName <$> foreignLibs pd
, condExecutables = mkCondTree' exeName <$> executables pd
, condTestSuites = mkCondTree' testName <$> testSuites pd
, condBenchmarks = mkCondTree' benchmarkName <$> benchmarks pd
}
where
mkCondTree x = CondNode x [] []
mkCondTreeL l = (fromMaybe (mkUnqualComponentName "") (libName l), CondNode l [] [])
mkCondTree'
:: (a -> UnqualComponentName)
-> a -> (UnqualComponentName, CondTree ConfVar [Dependency] a)
mkCondTree' f x = (f x, CondNode x [] [])
writeHookedBuildInfo :: FilePath -> HookedBuildInfo -> NoCallStackIO ()
writeHookedBuildInfo fpath = writeFileAtomic fpath . BS.Char8.pack
. showHookedBuildInfo
showHookedBuildInfo :: HookedBuildInfo -> String
showHookedBuildInfo (mb_lib_bi, ex_bis) = render $
maybe mempty (prettyFieldGrammar buildInfoFieldGrammar) mb_lib_bi
$$ vcat
[ space
$$ (text "executable:" <+> disp name)
$$ prettyFieldGrammar buildInfoFieldGrammar bi
| (name, bi) <- ex_bis
]