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