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