61a65797649517cc52177e85b10928a749ed61801900c9a0b81a052313b6c6a28699767da43248dcfc7baa90fceb162f9b481045214c575971dfe3d62a6421 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. # This file comes from
  2. # https://github.com/martine/ninja/blob/master/misc/ninja_syntax.py
  3. # Do not edit! Edit the upstream one instead.
  4. """Python module for generating .ninja files.
  5. Note that this is emphatically not a required piece of Ninja; it's
  6. just a helpful utility for build-file-generation systems that already
  7. use Python.
  8. """
  9. import textwrap
  10. def escape_path(word):
  11. return word.replace("$ ", "$$ ").replace(" ", "$ ").replace(":", "$:")
  12. class Writer:
  13. def __init__(self, output, width=78):
  14. self.output = output
  15. self.width = width
  16. def newline(self):
  17. self.output.write("\n")
  18. def comment(self, text):
  19. for line in textwrap.wrap(text, self.width - 2):
  20. self.output.write("# " + line + "\n")
  21. def variable(self, key, value, indent=0):
  22. if value is None:
  23. return
  24. if isinstance(value, list):
  25. value = " ".join(filter(None, value)) # Filter out empty strings.
  26. self._line(f"{key} = {value}", indent)
  27. def pool(self, name, depth):
  28. self._line("pool %s" % name)
  29. self.variable("depth", depth, indent=1)
  30. def rule(
  31. self,
  32. name,
  33. command,
  34. description=None,
  35. depfile=None,
  36. generator=False,
  37. pool=None,
  38. restat=False,
  39. rspfile=None,
  40. rspfile_content=None,
  41. deps=None,
  42. ):
  43. self._line("rule %s" % name)
  44. self.variable("command", command, indent=1)
  45. if description:
  46. self.variable("description", description, indent=1)
  47. if depfile:
  48. self.variable("depfile", depfile, indent=1)
  49. if generator:
  50. self.variable("generator", "1", indent=1)
  51. if pool:
  52. self.variable("pool", pool, indent=1)
  53. if restat:
  54. self.variable("restat", "1", indent=1)
  55. if rspfile:
  56. self.variable("rspfile", rspfile, indent=1)
  57. if rspfile_content:
  58. self.variable("rspfile_content", rspfile_content, indent=1)
  59. if deps:
  60. self.variable("deps", deps, indent=1)
  61. def build(
  62. self, outputs, rule, inputs=None, implicit=None, order_only=None, variables=None
  63. ):
  64. outputs = self._as_list(outputs)
  65. all_inputs = self._as_list(inputs)[:]
  66. out_outputs = list(map(escape_path, outputs))
  67. all_inputs = list(map(escape_path, all_inputs))
  68. if implicit:
  69. implicit = map(escape_path, self._as_list(implicit))
  70. all_inputs.append("|")
  71. all_inputs.extend(implicit)
  72. if order_only:
  73. order_only = map(escape_path, self._as_list(order_only))
  74. all_inputs.append("||")
  75. all_inputs.extend(order_only)
  76. self._line(
  77. "build {}: {}".format(" ".join(out_outputs), " ".join([rule] + all_inputs))
  78. )
  79. if variables:
  80. if isinstance(variables, dict):
  81. iterator = iter(variables.items())
  82. else:
  83. iterator = iter(variables)
  84. for key, val in iterator:
  85. self.variable(key, val, indent=1)
  86. return outputs
  87. def include(self, path):
  88. self._line("include %s" % path)
  89. def subninja(self, path):
  90. self._line("subninja %s" % path)
  91. def default(self, paths):
  92. self._line("default %s" % " ".join(self._as_list(paths)))
  93. def _count_dollars_before_index(self, s, i):
  94. """Returns the number of '$' characters right in front of s[i]."""
  95. dollar_count = 0
  96. dollar_index = i - 1
  97. while dollar_index > 0 and s[dollar_index] == "$":
  98. dollar_count += 1
  99. dollar_index -= 1
  100. return dollar_count
  101. def _line(self, text, indent=0):
  102. """Write 'text' word-wrapped at self.width characters."""
  103. leading_space = " " * indent
  104. while len(leading_space) + len(text) > self.width:
  105. # The text is too wide; wrap if possible.
  106. # Find the rightmost space that would obey our width constraint and
  107. # that's not an escaped space.
  108. available_space = self.width - len(leading_space) - len(" $")
  109. space = available_space
  110. while True:
  111. space = text.rfind(" ", 0, space)
  112. if space < 0 or self._count_dollars_before_index(text, space) % 2 == 0:
  113. break
  114. if space < 0:
  115. # No such space; just use the first unescaped space we can find.
  116. space = available_space - 1
  117. while True:
  118. space = text.find(" ", space + 1)
  119. if (
  120. space < 0
  121. or self._count_dollars_before_index(text, space) % 2 == 0
  122. ):
  123. break
  124. if space < 0:
  125. # Give up on breaking.
  126. break
  127. self.output.write(leading_space + text[0:space] + " $\n")
  128. text = text[space + 1 :]
  129. # Subsequent lines are continuations, so indent them.
  130. leading_space = " " * (indent + 2)
  131. self.output.write(leading_space + text + "\n")
  132. def _as_list(self, input):
  133. if input is None:
  134. return []
  135. if isinstance(input, list):
  136. return input
  137. return [input]
  138. def escape(string):
  139. """Escape a string such that it can be embedded into a Ninja file without
  140. further interpretation."""
  141. assert "\n" not in string, "Ninja syntax does not allow newlines"
  142. # We only have one special metacharacter: '$'.
  143. return string.replace("$", "$$")