1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
--- Module to pretty print Ninja build files
--- @author Marc Andre Wittorf

module Ninja.Pretty
  ( renderNinja, ppFile
  , ppDecl, ppDef, ppDefs, ppTargets, ppImplicitTargets, ppOrderOnlyTargets
  ) where

import Prelude hiding ( empty )

import Text.Pretty

import Ninja.Types


--- Render a Ninja file to a string representation
--- @param file the abstract Ninja file representation
--- @return     the rendered Ninja file
renderNinja :: File -> String
renderNinja = showWidth 15 . ppFile

--- Render a Ninja file to a Pretty-Doc
--- @param file the abstract Ninja file representation
--- @return     the rendered Ninja file
ppFile :: File -> Doc
ppFile (File decls) = (vsepBlank $ map ppDecl decls) <> hardline

--- Render a Ninja declaration
--- @param decl a Ninja declaration
--- @param      the rendered Ninja declaration
ppDecl :: Decl -> Doc
ppDecl (Rule n ds)                     = nest 2 $ text "rule" <+> text n $$
                                                  ppDefs ds
ppDecl (Edge r eos ios eis iis ois ds) = nest 2 $
                                         text "build" <+>
                                         ppTargets eos <+>
                                         ppImplicitTargets ios <>
                                         colon <+>
                                         text r <+>
                                         ppTargets eis <+>
                                         ppImplicitTargets iis <+>
                                         ppOrderOnlyTargets ois $$ ppDefs ds
ppDecl (Var d)                         = ppDef d
ppDecl (Default t)                     = text "default" <+> text t
ppDecl (Subninja p)                    = text "subninja" <+> text p
ppDecl (Include p)                     = text "include" <+> text p
ppDecl (Pool n ds)                     = nest 2 $ text "pool" <+> text n $$
                                                  ppDefs ds


--- Render a defintion
--- @param def the definition
--- @return    the rendered definition
ppDef :: Def -> Doc
ppDef (k,v) = text k <+> equals <+> text v

--- Render definitions
--- @param defs the definitions
--- @return     the rendered definitions
ppDefs :: [Def] -> Doc
ppDefs = vsep . map ppDef

--- Render explicit targets
--- @param tgts the targets
--- @return     the rendered targets
ppTargets :: [Target] -> Doc
ppTargets = hsep . map (text . escapeFilenames)

--- Render implicit targets
--- @param tgts the targets
--- @return     the rendered targets
ppImplicitTargets :: [Target] -> Doc
ppImplicitTargets []       = empty
ppImplicitTargets ts@(_:_) = char '|' <+> ppTargets ts

--- Render order-only targets
--- @param tgts the targets
--- @return     the rendered targets
ppOrderOnlyTargets :: [Target] -> Doc
ppOrderOnlyTargets []       = empty
ppOrderOnlyTargets ts@(_:_) = text "||" <+> ppTargets ts

--- Escape filenames (replace spaces, dollars, line breaks and colons)
--- @param tgt a target
--- @return    the escaped target
escapeFilenames :: Target -> Target
escapeFilenames = concatMap $ \c -> case c of
                                         ' '  -> "$ "
                                         '$'  -> "$$"
                                         '\n' -> "$\n"
                                         ':'  -> "$:"
                                         _    -> [c]

-- -----------------------------------------------------------------------------