preprocessed_builds.yaml.gen.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 # Copyright 2019 gRPC authors.
4 #
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 
17 import collections
18 import os
19 import re
20 import subprocess
21 import xml.etree.ElementTree as ET
22 import yaml
23 
24 ABSEIL_PATH = "third_party/abseil-cpp"
25 OUTPUT_PATH = "src/abseil-cpp/preprocessed_builds.yaml"
26 CAPITAL_WORD = re.compile("[A-Z]+")
27 ABSEIL_CMAKE_RULE_BEGIN = re.compile("^absl_cc_.*\(", re.MULTILINE)
28 ABSEIL_CMAKE_RULE_END = re.compile("^\)", re.MULTILINE)
29 
30 # Rule object representing the rule of Bazel BUILD.
31 Rule = collections.namedtuple(
32  "Rule", "type name package srcs hdrs textual_hdrs deps visibility testonly")
33 
34 
35 def get_elem_value(elem, name):
36  """Returns the value of XML element with the given name."""
37  for child in elem:
38  if child.attrib.get("name") == name:
39  if child.tag == "string":
40  return child.attrib.get("value")
41  elif child.tag == "boolean":
42  return child.attrib.get("value") == "true"
43  elif child.tag == "list":
44  return [nested_child.attrib.get("value") for nested_child in child]
45  else:
46  raise "Cannot recognize tag: " + child.tag
47  return None
48 
49 
50 def normalize_paths(paths):
51  """Returns the list of normalized path."""
52  # e.g. ["//absl/strings:dir/header.h"] -> ["absl/strings/dir/header.h"]
53  return [path.lstrip("/").replace(":", "/") for path in paths]
54 
55 
56 def parse_bazel_rule(elem, package):
57  """Returns a rule from bazel XML rule."""
58  return Rule(
59  type=elem.attrib["class"],
60  name=get_elem_value(elem, "name"),
61  package=package,
62  srcs=normalize_paths(get_elem_value(elem, "srcs") or []),
63  hdrs=normalize_paths(get_elem_value(elem, "hdrs") or []),
64  textual_hdrs=normalize_paths(get_elem_value(elem, "textual_hdrs") or []),
65  deps=get_elem_value(elem, "deps") or [],
66  visibility=get_elem_value(elem, "visibility") or [],
67  testonly=get_elem_value(elem, "testonly") or False)
68 
69 
70 def read_bazel_build(package):
71  """Runs bazel query on given package file and returns all cc rules."""
72  # Use a wrapper version of bazel in gRPC not to use system-wide bazel
73  # to avoid bazel conflict when running on Kokoro.
74  BAZEL_BIN = "../../tools/bazel"
75  result = subprocess.check_output(
76  [BAZEL_BIN, "query", package + ":all", "--output", "xml"])
77  root = ET.fromstring(result)
78  return [
79  parse_bazel_rule(elem, package)
80  for elem in root
81  if elem.tag == "rule" and elem.attrib["class"].startswith("cc_")
82  ]
83 
84 
85 def collect_bazel_rules(root_path):
86  """Collects and returns all bazel rules from root path recursively."""
87  rules = []
88  for cur, _, _ in os.walk(root_path):
89  build_path = os.path.join(cur, "BUILD.bazel")
90  if os.path.exists(build_path):
91  rules.extend(read_bazel_build("//" + cur))
92  return rules
93 
94 
95 def parse_cmake_rule(rule, package):
96  """Returns a rule from absl cmake rule.
97  Reference: https://github.com/abseil/abseil-cpp/blob/master/CMake/AbseilHelpers.cmake
98  """
99  kv = {}
100  bucket = None
101  lines = rule.splitlines()
102  for line in lines[1:-1]:
103  if CAPITAL_WORD.match(line.strip()):
104  bucket = kv.setdefault(line.strip(), [])
105  else:
106  if bucket is not None:
107  bucket.append(line.strip())
108  else:
109  raise ValueError("Illegal syntax: {}".format(rule))
110  return Rule(
111  type=lines[0].rstrip("("),
112  name="absl::" + kv["NAME"][0],
113  package=package,
114  srcs=[package + "/" + f.strip('"') for f in kv.get("SRCS", [])],
115  hdrs=[package + "/" + f.strip('"') for f in kv.get("HDRS", [])],
116  textual_hdrs=[],
117  deps=kv.get("DEPS", []),
118  visibility="PUBLIC" in kv,
119  testonly="TESTONLY" in kv,
120  )
121 
122 
123 def read_cmake_build(build_path, package):
124  """Parses given CMakeLists.txt file and returns all cc rules."""
125  rules = []
126  with open(build_path, "r") as f:
127  src = f.read()
128  for begin_mo in ABSEIL_CMAKE_RULE_BEGIN.finditer(src):
129  end_mo = ABSEIL_CMAKE_RULE_END.search(src[begin_mo.start(0):])
130  expr = src[begin_mo.start(0):begin_mo.start(0) + end_mo.start(0) + 1]
131  rules.append(parse_cmake_rule(expr, package))
132  return rules
133 
134 
135 def collect_cmake_rules(root_path):
136  """Collects and returns all cmake rules from root path recursively."""
137  rules = []
138  for cur, _, _ in os.walk(root_path):
139  build_path = os.path.join(cur, "CMakeLists.txt")
140  if os.path.exists(build_path):
141  rules.extend(read_cmake_build(build_path, cur))
142  return rules
143 
144 
145 def pairing_bazel_and_cmake_rules(bazel_rules, cmake_rules):
146  """Returns a pair map between bazel rules and cmake rules based on
147  the similarity of the file list in the rule. This is because
148  cmake build and bazel build of abseil are not identical.
149  """
150  pair_map = {}
151  for rule in bazel_rules:
152  best_crule, best_similarity = None, 0
153  for crule in cmake_rules:
154  similarity = len(
155  set(rule.srcs + rule.hdrs + rule.textual_hdrs).intersection(
156  set(crule.srcs + crule.hdrs + crule.textual_hdrs)))
157  if similarity > best_similarity:
158  best_crule, best_similarity = crule, similarity
159  if best_crule:
160  pair_map[(rule.package, rule.name)] = best_crule.name
161  return pair_map
162 
163 
164 def resolve_hdrs(files):
165  return [ABSEIL_PATH + "/" + f for f in files if f.endswith((".h", ".inc"))]
166 
167 
168 def resolve_srcs(files):
169  return [ABSEIL_PATH + "/" + f for f in files if f.endswith(".cc")]
170 
171 
172 def resolve_deps(targets):
173  return [(t[2:] if t.startswith("//") else t) for t in targets]
174 
175 
176 def generate_builds(root_path):
177  """Generates builds from all BUILD files under absl directory."""
178  bazel_rules = list(
179  filter(lambda r: r.type == "cc_library" and not r.testonly,
180  collect_bazel_rules(root_path)))
181  cmake_rules = list(
182  filter(lambda r: r.type == "absl_cc_library" and not r.testonly,
183  collect_cmake_rules(root_path)))
184  pair_map = pairing_bazel_and_cmake_rules(bazel_rules, cmake_rules)
185  builds = []
186  for rule in sorted(bazel_rules, key=lambda r: r.package[2:] + ":" + r.name):
187  p = {
188  "name":
189  rule.package[2:] + ":" + rule.name,
190  "cmake_target":
191  pair_map.get((rule.package, rule.name)) or "",
192  "headers":
193  sorted(resolve_hdrs(rule.srcs + rule.hdrs + rule.textual_hdrs)),
194  "src":
195  sorted(resolve_srcs(rule.srcs + rule.hdrs + rule.textual_hdrs)),
196  "deps":
197  sorted(resolve_deps(rule.deps)),
198  }
199  builds.append(p)
200  return builds
201 
202 
203 def main():
204  previous_dir = os.getcwd()
205  os.chdir(ABSEIL_PATH)
206  builds = generate_builds("absl")
207  os.chdir(previous_dir)
208  with open(OUTPUT_PATH, 'w') as outfile:
209  outfile.write(yaml.dump(builds, indent=2))
210 
211 
212 if __name__ == "__main__":
213  main()
preprocessed_builds.resolve_hdrs
def resolve_hdrs(files)
Definition: preprocessed_builds.yaml.gen.py:164
http2_test_server.format
format
Definition: http2_test_server.py:118
preprocessed_builds.normalize_paths
def normalize_paths(paths)
Definition: preprocessed_builds.yaml.gen.py:50
preprocessed_builds.resolve_deps
def resolve_deps(targets)
Definition: preprocessed_builds.yaml.gen.py:172
preprocessed_builds.parse_bazel_rule
def parse_bazel_rule(elem, package)
Definition: preprocessed_builds.yaml.gen.py:56
preprocessed_builds.parse_cmake_rule
def parse_cmake_rule(rule, package)
Definition: preprocessed_builds.yaml.gen.py:95
preprocessed_builds.Rule
Rule
Definition: preprocessed_builds.yaml.gen.py:31
preprocessed_builds.get_elem_value
def get_elem_value(elem, name)
Definition: preprocessed_builds.yaml.gen.py:35
preprocessed_builds.read_bazel_build
def read_bazel_build(package)
Definition: preprocessed_builds.yaml.gen.py:70
preprocessed_builds.main
def main()
Definition: preprocessed_builds.yaml.gen.py:203
preprocessed_builds.generate_builds
def generate_builds(root_path)
Definition: preprocessed_builds.yaml.gen.py:176
preprocessed_builds.read_cmake_build
def read_cmake_build(build_path, package)
Definition: preprocessed_builds.yaml.gen.py:123
main
Definition: main.py:1
cpp.gmock_class.set
set
Definition: bloaty/third_party/googletest/googlemock/scripts/generator/cpp/gmock_class.py:44
preprocessed_builds.collect_cmake_rules
def collect_cmake_rules(root_path)
Definition: preprocessed_builds.yaml.gen.py:135
open
#define open
Definition: test-fs.c:46
preprocessed_builds.resolve_srcs
def resolve_srcs(files)
Definition: preprocessed_builds.yaml.gen.py:168
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
preprocessed_builds.collect_bazel_rules
def collect_bazel_rules(root_path)
Definition: preprocessed_builds.yaml.gen.py:85
preprocessed_builds.pairing_bazel_and_cmake_rules
def pairing_bazel_and_cmake_rules(bazel_rules, cmake_rules)
Definition: preprocessed_builds.yaml.gen.py:145


grpc
Author(s):
autogenerated on Fri May 16 2025 02:59:45