1*8975f5c5SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*8975f5c5SAndroid Build Coastguard Worker# -*- coding: utf-8 -*- 3*8975f5c5SAndroid Build Coastguard Worker"""This script generates abseil.podspec from all BUILD.bazel files. 4*8975f5c5SAndroid Build Coastguard Worker 5*8975f5c5SAndroid Build Coastguard WorkerThis is expected to run on abseil git repository with Bazel 1.0 on Linux. 6*8975f5c5SAndroid Build Coastguard WorkerIt recursively analyzes BUILD.bazel files using query command of Bazel to 7*8975f5c5SAndroid Build Coastguard Workerdump its build rules in XML format. From these rules, it constructs podspec 8*8975f5c5SAndroid Build Coastguard Workerstructure. 9*8975f5c5SAndroid Build Coastguard Worker""" 10*8975f5c5SAndroid Build Coastguard Worker 11*8975f5c5SAndroid Build Coastguard Workerimport argparse 12*8975f5c5SAndroid Build Coastguard Workerimport collections 13*8975f5c5SAndroid Build Coastguard Workerimport os 14*8975f5c5SAndroid Build Coastguard Workerimport re 15*8975f5c5SAndroid Build Coastguard Workerimport subprocess 16*8975f5c5SAndroid Build Coastguard Workerimport xml.etree.ElementTree 17*8975f5c5SAndroid Build Coastguard Worker 18*8975f5c5SAndroid Build Coastguard Worker# Template of root podspec. 19*8975f5c5SAndroid Build Coastguard WorkerSPEC_TEMPLATE = """ 20*8975f5c5SAndroid Build Coastguard Worker# This file has been automatically generated from a script. 21*8975f5c5SAndroid Build Coastguard Worker# Please make modifications to `abseil.podspec.gen.py` instead. 22*8975f5c5SAndroid Build Coastguard WorkerPod::Spec.new do |s| 23*8975f5c5SAndroid Build Coastguard Worker s.name = 'abseil' 24*8975f5c5SAndroid Build Coastguard Worker s.version = '${version}' 25*8975f5c5SAndroid Build Coastguard Worker s.summary = 'Abseil Common Libraries (C++) from Google' 26*8975f5c5SAndroid Build Coastguard Worker s.homepage = 'https://abseil.io' 27*8975f5c5SAndroid Build Coastguard Worker s.license = 'Apache License, Version 2.0' 28*8975f5c5SAndroid Build Coastguard Worker s.authors = { 'Abseil Team' => '[email protected]' } 29*8975f5c5SAndroid Build Coastguard Worker s.source = { 30*8975f5c5SAndroid Build Coastguard Worker :git => 'https://github.com/abseil/abseil-cpp.git', 31*8975f5c5SAndroid Build Coastguard Worker :tag => '${tag}', 32*8975f5c5SAndroid Build Coastguard Worker } 33*8975f5c5SAndroid Build Coastguard Worker s.resource_bundles = { 34*8975f5c5SAndroid Build Coastguard Worker s.module_name => 'PrivacyInfo.xcprivacy', 35*8975f5c5SAndroid Build Coastguard Worker } 36*8975f5c5SAndroid Build Coastguard Worker s.module_name = 'absl' 37*8975f5c5SAndroid Build Coastguard Worker s.header_mappings_dir = 'absl' 38*8975f5c5SAndroid Build Coastguard Worker s.header_dir = 'absl' 39*8975f5c5SAndroid Build Coastguard Worker s.libraries = 'c++' 40*8975f5c5SAndroid Build Coastguard Worker s.compiler_flags = '-Wno-everything' 41*8975f5c5SAndroid Build Coastguard Worker s.pod_target_xcconfig = { 42*8975f5c5SAndroid Build Coastguard Worker 'USER_HEADER_SEARCH_PATHS' => '$(inherited) "$(PODS_TARGET_SRCROOT)"', 43*8975f5c5SAndroid Build Coastguard Worker 'USE_HEADERMAP' => 'NO', 44*8975f5c5SAndroid Build Coastguard Worker 'ALWAYS_SEARCH_USER_PATHS' => 'NO', 45*8975f5c5SAndroid Build Coastguard Worker } 46*8975f5c5SAndroid Build Coastguard Worker s.ios.deployment_target = '9.0' 47*8975f5c5SAndroid Build Coastguard Worker s.osx.deployment_target = '10.11' 48*8975f5c5SAndroid Build Coastguard Worker s.tvos.deployment_target = '9.0' 49*8975f5c5SAndroid Build Coastguard Worker s.watchos.deployment_target = '2.0' 50*8975f5c5SAndroid Build Coastguard Worker s.subspec 'xcprivacy' do |ss| 51*8975f5c5SAndroid Build Coastguard Worker ss.resource_bundles = { 52*8975f5c5SAndroid Build Coastguard Worker ss.module_name => 'PrivacyInfo.xcprivacy', 53*8975f5c5SAndroid Build Coastguard Worker } 54*8975f5c5SAndroid Build Coastguard Worker end 55*8975f5c5SAndroid Build Coastguard Worker""" 56*8975f5c5SAndroid Build Coastguard Worker 57*8975f5c5SAndroid Build Coastguard Worker# Rule object representing the rule of Bazel BUILD. 58*8975f5c5SAndroid Build Coastguard WorkerRule = collections.namedtuple( 59*8975f5c5SAndroid Build Coastguard Worker "Rule", "type name package srcs hdrs textual_hdrs deps visibility testonly") 60*8975f5c5SAndroid Build Coastguard Worker 61*8975f5c5SAndroid Build Coastguard Worker 62*8975f5c5SAndroid Build Coastguard Workerdef get_elem_value(elem, name): 63*8975f5c5SAndroid Build Coastguard Worker """Returns the value of XML element with the given name.""" 64*8975f5c5SAndroid Build Coastguard Worker for child in elem: 65*8975f5c5SAndroid Build Coastguard Worker if child.attrib.get("name") != name: 66*8975f5c5SAndroid Build Coastguard Worker continue 67*8975f5c5SAndroid Build Coastguard Worker if child.tag == "string": 68*8975f5c5SAndroid Build Coastguard Worker return child.attrib.get("value") 69*8975f5c5SAndroid Build Coastguard Worker if child.tag == "boolean": 70*8975f5c5SAndroid Build Coastguard Worker return child.attrib.get("value") == "true" 71*8975f5c5SAndroid Build Coastguard Worker if child.tag == "list": 72*8975f5c5SAndroid Build Coastguard Worker return [nested_child.attrib.get("value") for nested_child in child] 73*8975f5c5SAndroid Build Coastguard Worker raise "Cannot recognize tag: " + child.tag 74*8975f5c5SAndroid Build Coastguard Worker return None 75*8975f5c5SAndroid Build Coastguard Worker 76*8975f5c5SAndroid Build Coastguard Worker 77*8975f5c5SAndroid Build Coastguard Workerdef normalize_paths(paths): 78*8975f5c5SAndroid Build Coastguard Worker """Returns the list of normalized path.""" 79*8975f5c5SAndroid Build Coastguard Worker # e.g. ["//absl/strings:dir/header.h"] -> ["absl/strings/dir/header.h"] 80*8975f5c5SAndroid Build Coastguard Worker return [path.lstrip("/").replace(":", "/") for path in paths] 81*8975f5c5SAndroid Build Coastguard Worker 82*8975f5c5SAndroid Build Coastguard Worker 83*8975f5c5SAndroid Build Coastguard Workerdef parse_rule(elem, package): 84*8975f5c5SAndroid Build Coastguard Worker """Returns a rule from bazel XML rule.""" 85*8975f5c5SAndroid Build Coastguard Worker return Rule( 86*8975f5c5SAndroid Build Coastguard Worker type=elem.attrib["class"], 87*8975f5c5SAndroid Build Coastguard Worker name=get_elem_value(elem, "name"), 88*8975f5c5SAndroid Build Coastguard Worker package=package, 89*8975f5c5SAndroid Build Coastguard Worker srcs=normalize_paths(get_elem_value(elem, "srcs") or []), 90*8975f5c5SAndroid Build Coastguard Worker hdrs=normalize_paths(get_elem_value(elem, "hdrs") or []), 91*8975f5c5SAndroid Build Coastguard Worker textual_hdrs=normalize_paths(get_elem_value(elem, "textual_hdrs") or []), 92*8975f5c5SAndroid Build Coastguard Worker deps=get_elem_value(elem, "deps") or [], 93*8975f5c5SAndroid Build Coastguard Worker visibility=get_elem_value(elem, "visibility") or [], 94*8975f5c5SAndroid Build Coastguard Worker testonly=get_elem_value(elem, "testonly") or False) 95*8975f5c5SAndroid Build Coastguard Worker 96*8975f5c5SAndroid Build Coastguard Worker 97*8975f5c5SAndroid Build Coastguard Workerdef read_build(package): 98*8975f5c5SAndroid Build Coastguard Worker """Runs bazel query on given package file and returns all cc rules.""" 99*8975f5c5SAndroid Build Coastguard Worker result = subprocess.check_output( 100*8975f5c5SAndroid Build Coastguard Worker ["bazel", "query", package + ":all", "--output", "xml"]) 101*8975f5c5SAndroid Build Coastguard Worker root = xml.etree.ElementTree.fromstring(result) 102*8975f5c5SAndroid Build Coastguard Worker return [ 103*8975f5c5SAndroid Build Coastguard Worker parse_rule(elem, package) 104*8975f5c5SAndroid Build Coastguard Worker for elem in root 105*8975f5c5SAndroid Build Coastguard Worker if elem.tag == "rule" and elem.attrib["class"].startswith("cc_") 106*8975f5c5SAndroid Build Coastguard Worker ] 107*8975f5c5SAndroid Build Coastguard Worker 108*8975f5c5SAndroid Build Coastguard Worker 109*8975f5c5SAndroid Build Coastguard Workerdef collect_rules(root_path): 110*8975f5c5SAndroid Build Coastguard Worker """Collects and returns all rules from root path recursively.""" 111*8975f5c5SAndroid Build Coastguard Worker rules = [] 112*8975f5c5SAndroid Build Coastguard Worker for cur, _, _ in os.walk(root_path): 113*8975f5c5SAndroid Build Coastguard Worker build_path = os.path.join(cur, "BUILD.bazel") 114*8975f5c5SAndroid Build Coastguard Worker if os.path.exists(build_path): 115*8975f5c5SAndroid Build Coastguard Worker rules.extend(read_build("//" + cur)) 116*8975f5c5SAndroid Build Coastguard Worker return rules 117*8975f5c5SAndroid Build Coastguard Worker 118*8975f5c5SAndroid Build Coastguard Worker 119*8975f5c5SAndroid Build Coastguard Workerdef relevant_rule(rule): 120*8975f5c5SAndroid Build Coastguard Worker """Returns true if a given rule is relevant when generating a podspec.""" 121*8975f5c5SAndroid Build Coastguard Worker return ( 122*8975f5c5SAndroid Build Coastguard Worker # cc_library only (ignore cc_test, cc_binary) 123*8975f5c5SAndroid Build Coastguard Worker rule.type == "cc_library" and 124*8975f5c5SAndroid Build Coastguard Worker # ignore empty rule 125*8975f5c5SAndroid Build Coastguard Worker (rule.hdrs + rule.textual_hdrs + rule.srcs) and 126*8975f5c5SAndroid Build Coastguard Worker # ignore test-only rule 127*8975f5c5SAndroid Build Coastguard Worker not rule.testonly) 128*8975f5c5SAndroid Build Coastguard Worker 129*8975f5c5SAndroid Build Coastguard Worker 130*8975f5c5SAndroid Build Coastguard Workerdef get_spec_var(depth): 131*8975f5c5SAndroid Build Coastguard Worker """Returns the name of variable for spec with given depth.""" 132*8975f5c5SAndroid Build Coastguard Worker return "s" if depth == 0 else "s{}".format(depth) 133*8975f5c5SAndroid Build Coastguard Worker 134*8975f5c5SAndroid Build Coastguard Worker 135*8975f5c5SAndroid Build Coastguard Workerdef get_spec_name(label): 136*8975f5c5SAndroid Build Coastguard Worker """Converts the label of bazel rule to the name of podspec.""" 137*8975f5c5SAndroid Build Coastguard Worker assert label.startswith("//absl/"), "{} doesn't start with //absl/".format( 138*8975f5c5SAndroid Build Coastguard Worker label) 139*8975f5c5SAndroid Build Coastguard Worker # e.g. //absl/apple/banana -> abseil/apple/banana 140*8975f5c5SAndroid Build Coastguard Worker return "abseil/" + label[7:] 141*8975f5c5SAndroid Build Coastguard Worker 142*8975f5c5SAndroid Build Coastguard Worker 143*8975f5c5SAndroid Build Coastguard Workerdef write_podspec(f, rules, args): 144*8975f5c5SAndroid Build Coastguard Worker """Writes a podspec from given rules and args.""" 145*8975f5c5SAndroid Build Coastguard Worker rule_dir = build_rule_directory(rules)["abseil"] 146*8975f5c5SAndroid Build Coastguard Worker # Write root part with given arguments 147*8975f5c5SAndroid Build Coastguard Worker spec = re.sub(r"\$\{(\w+)\}", lambda x: args[x.group(1)], 148*8975f5c5SAndroid Build Coastguard Worker SPEC_TEMPLATE).lstrip() 149*8975f5c5SAndroid Build Coastguard Worker f.write(spec) 150*8975f5c5SAndroid Build Coastguard Worker # Write all target rules 151*8975f5c5SAndroid Build Coastguard Worker write_podspec_map(f, rule_dir, 0) 152*8975f5c5SAndroid Build Coastguard Worker f.write("end\n") 153*8975f5c5SAndroid Build Coastguard Worker 154*8975f5c5SAndroid Build Coastguard Worker 155*8975f5c5SAndroid Build Coastguard Workerdef build_rule_directory(rules): 156*8975f5c5SAndroid Build Coastguard Worker """Builds a tree-style rule directory from given rules.""" 157*8975f5c5SAndroid Build Coastguard Worker rule_dir = {} 158*8975f5c5SAndroid Build Coastguard Worker for rule in rules: 159*8975f5c5SAndroid Build Coastguard Worker cur = rule_dir 160*8975f5c5SAndroid Build Coastguard Worker for frag in get_spec_name(rule.package).split("/"): 161*8975f5c5SAndroid Build Coastguard Worker cur = cur.setdefault(frag, {}) 162*8975f5c5SAndroid Build Coastguard Worker cur[rule.name] = rule 163*8975f5c5SAndroid Build Coastguard Worker return rule_dir 164*8975f5c5SAndroid Build Coastguard Worker 165*8975f5c5SAndroid Build Coastguard Worker 166*8975f5c5SAndroid Build Coastguard Workerdef write_podspec_map(f, cur_map, depth): 167*8975f5c5SAndroid Build Coastguard Worker """Writes podspec from rule map recursively.""" 168*8975f5c5SAndroid Build Coastguard Worker for key, value in sorted(cur_map.items()): 169*8975f5c5SAndroid Build Coastguard Worker indent = " " * (depth + 1) 170*8975f5c5SAndroid Build Coastguard Worker f.write("{indent}{var0}.subspec '{key}' do |{var1}|\n".format( 171*8975f5c5SAndroid Build Coastguard Worker indent=indent, 172*8975f5c5SAndroid Build Coastguard Worker key=key, 173*8975f5c5SAndroid Build Coastguard Worker var0=get_spec_var(depth), 174*8975f5c5SAndroid Build Coastguard Worker var1=get_spec_var(depth + 1))) 175*8975f5c5SAndroid Build Coastguard Worker if isinstance(value, dict): 176*8975f5c5SAndroid Build Coastguard Worker write_podspec_map(f, value, depth + 1) 177*8975f5c5SAndroid Build Coastguard Worker else: 178*8975f5c5SAndroid Build Coastguard Worker write_podspec_rule(f, value, depth + 1) 179*8975f5c5SAndroid Build Coastguard Worker f.write("{indent}end\n".format(indent=indent)) 180*8975f5c5SAndroid Build Coastguard Worker 181*8975f5c5SAndroid Build Coastguard Worker 182*8975f5c5SAndroid Build Coastguard Workerdef write_podspec_rule(f, rule, depth): 183*8975f5c5SAndroid Build Coastguard Worker """Writes podspec from given rule.""" 184*8975f5c5SAndroid Build Coastguard Worker indent = " " * (depth + 1) 185*8975f5c5SAndroid Build Coastguard Worker spec_var = get_spec_var(depth) 186*8975f5c5SAndroid Build Coastguard Worker # Puts all files in hdrs, textual_hdrs, and srcs into source_files. 187*8975f5c5SAndroid Build Coastguard Worker # Since CocoaPods treats header_files a bit differently from bazel, 188*8975f5c5SAndroid Build Coastguard Worker # this won't generate a header_files field so that all source_files 189*8975f5c5SAndroid Build Coastguard Worker # are considered as header files. 190*8975f5c5SAndroid Build Coastguard Worker srcs = sorted(set(rule.hdrs + rule.textual_hdrs + rule.srcs)) 191*8975f5c5SAndroid Build Coastguard Worker write_indented_list( 192*8975f5c5SAndroid Build Coastguard Worker f, "{indent}{var}.source_files = ".format(indent=indent, var=spec_var), 193*8975f5c5SAndroid Build Coastguard Worker srcs) 194*8975f5c5SAndroid Build Coastguard Worker # Writes dependencies of this rule. 195*8975f5c5SAndroid Build Coastguard Worker for dep in sorted(rule.deps): 196*8975f5c5SAndroid Build Coastguard Worker name = get_spec_name(dep.replace(":", "/")) 197*8975f5c5SAndroid Build Coastguard Worker f.write("{indent}{var}.dependency '{dep}'\n".format( 198*8975f5c5SAndroid Build Coastguard Worker indent=indent, var=spec_var, dep=name)) 199*8975f5c5SAndroid Build Coastguard Worker # Writes dependency to xcprivacy 200*8975f5c5SAndroid Build Coastguard Worker f.write( 201*8975f5c5SAndroid Build Coastguard Worker "{indent}{var}.dependency '{dep}'\n".format( 202*8975f5c5SAndroid Build Coastguard Worker indent=indent, var=spec_var, dep="abseil/xcprivacy" 203*8975f5c5SAndroid Build Coastguard Worker ) 204*8975f5c5SAndroid Build Coastguard Worker ) 205*8975f5c5SAndroid Build Coastguard Worker 206*8975f5c5SAndroid Build Coastguard Worker 207*8975f5c5SAndroid Build Coastguard Workerdef write_indented_list(f, leading, values): 208*8975f5c5SAndroid Build Coastguard Worker """Writes leading values in an indented style.""" 209*8975f5c5SAndroid Build Coastguard Worker f.write(leading) 210*8975f5c5SAndroid Build Coastguard Worker f.write((",\n" + " " * len(leading)).join("'{}'".format(v) for v in values)) 211*8975f5c5SAndroid Build Coastguard Worker f.write("\n") 212*8975f5c5SAndroid Build Coastguard Worker 213*8975f5c5SAndroid Build Coastguard Worker 214*8975f5c5SAndroid Build Coastguard Workerdef generate(args): 215*8975f5c5SAndroid Build Coastguard Worker """Generates a podspec file from all BUILD files under absl directory.""" 216*8975f5c5SAndroid Build Coastguard Worker rules = filter(relevant_rule, collect_rules("absl")) 217*8975f5c5SAndroid Build Coastguard Worker with open(args.output, "wt") as f: 218*8975f5c5SAndroid Build Coastguard Worker write_podspec(f, rules, vars(args)) 219*8975f5c5SAndroid Build Coastguard Worker 220*8975f5c5SAndroid Build Coastguard Worker 221*8975f5c5SAndroid Build Coastguard Workerdef main(): 222*8975f5c5SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 223*8975f5c5SAndroid Build Coastguard Worker description="Generates abseil.podspec from BUILD.bazel") 224*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 225*8975f5c5SAndroid Build Coastguard Worker "-v", "--version", help="The version of podspec", required=True) 226*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 227*8975f5c5SAndroid Build Coastguard Worker "-t", 228*8975f5c5SAndroid Build Coastguard Worker "--tag", 229*8975f5c5SAndroid Build Coastguard Worker default=None, 230*8975f5c5SAndroid Build Coastguard Worker help="The name of git tag (default: version)") 231*8975f5c5SAndroid Build Coastguard Worker parser.add_argument( 232*8975f5c5SAndroid Build Coastguard Worker "-o", 233*8975f5c5SAndroid Build Coastguard Worker "--output", 234*8975f5c5SAndroid Build Coastguard Worker default="abseil.podspec", 235*8975f5c5SAndroid Build Coastguard Worker help="The name of output file (default: abseil.podspec)") 236*8975f5c5SAndroid Build Coastguard Worker args = parser.parse_args() 237*8975f5c5SAndroid Build Coastguard Worker if args.tag is None: 238*8975f5c5SAndroid Build Coastguard Worker args.tag = args.version 239*8975f5c5SAndroid Build Coastguard Worker generate(args) 240*8975f5c5SAndroid Build Coastguard Worker 241*8975f5c5SAndroid Build Coastguard Worker 242*8975f5c5SAndroid Build Coastguard Workerif __name__ == "__main__": 243*8975f5c5SAndroid Build Coastguard Worker main() 244