aboutsummaryrefslogtreecommitdiffhomepage
path: root/absl/abseil.podspec.gen.py
diff options
context:
space:
mode:
Diffstat (limited to 'absl/abseil.podspec.gen.py')
-rwxr-xr-xabsl/abseil.podspec.gen.py247
1 files changed, 247 insertions, 0 deletions
diff --git a/absl/abseil.podspec.gen.py b/absl/abseil.podspec.gen.py
new file mode 100755
index 0000000..2bf153c
--- /dev/null
+++ b/absl/abseil.podspec.gen.py
@@ -0,0 +1,247 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""This script generates abseil.podspec from all BUILD.bazel files.
+
+This is expected to run on abseil git repository with Bazel 1.0 on Linux.
+It recursively analyzes BUILD.bazel files using query command of Bazel to
+dump its build rules in XML format. From these rules, it constructs podspec
+structure.
+"""
+
+import argparse
+import collections
+import os
+import re
+import subprocess
+import xml.etree.ElementTree
+
+# Template of root podspec.
+SPEC_TEMPLATE = """
+# This file has been automatically generated from a script.
+# Please make modifications to `abseil.podspec.gen.py` instead.
+Pod::Spec.new do |s|
+ s.name = 'abseil'
+ s.version = '${version}'
+ s.summary = 'Abseil Common Libraries (C++) from Google'
+ s.homepage = 'https://abseil.io'
+ s.license = 'Apache License, Version 2.0'
+ s.authors = { 'Abseil Team' => 'abseil-io@googlegroups.com' }
+ s.source = {
+ :git => 'https://github.com/abseil/abseil-cpp.git',
+ :tag => '${tag}',
+ }
+ s.module_name = 'absl'
+ s.header_mappings_dir = 'absl'
+ s.header_dir = 'absl'
+ s.libraries = 'c++'
+ s.compiler_flags = '-Wno-everything'
+ s.pod_target_xcconfig = {
+ 'USER_HEADER_SEARCH_PATHS' => '$(inherited) "$(PODS_TARGET_SRCROOT)"',
+ 'USE_HEADERMAP' => 'NO',
+ 'ALWAYS_SEARCH_USER_PATHS' => 'NO',
+ }
+ s.ios.deployment_target = '7.0'
+ s.osx.deployment_target = '10.9'
+ s.tvos.deployment_target = '9.0'
+ s.watchos.deployment_target = '2.0'
+"""
+
+# Limited platforms that abseil supports.
+# This is mainly because of sigaltstack unavailable on watchOS.
+LIMITED_SUPPORT_PLATFORMS = [
+ "ios.deployment_target = '7.0'",
+ "osx.deployment_target = '10.9'",
+]
+
+# Custom specification per rule.
+CUSTOM_SPEC_MAP = {
+ "//absl/debugging:failure_signal_handler": LIMITED_SUPPORT_PLATFORMS,
+}
+
+# Rule object representing the rule of Bazel BUILD.
+Rule = collections.namedtuple(
+ "Rule", "type name package srcs hdrs textual_hdrs deps visibility testonly")
+
+
+def get_elem_value(elem, name):
+ """Returns the value of XML element with the given name."""
+ for child in elem:
+ if child.attrib.get("name") != name:
+ continue
+ if child.tag == "string":
+ return child.attrib.get("value")
+ if child.tag == "boolean":
+ return child.attrib.get("value") == "true"
+ if child.tag == "list":
+ return [nested_child.attrib.get("value") for nested_child in child]
+ raise "Cannot recognize tag: " + child.tag
+ return None
+
+
+def normalize_paths(paths):
+ """Returns the list of normalized path."""
+ # e.g. ["//absl/strings:dir/header.h"] -> ["absl/strings/dir/header.h"]
+ return [path.lstrip("/").replace(":", "/") for path in paths]
+
+
+def parse_rule(elem, package):
+ """Returns a rule from bazel XML rule."""
+ return Rule(
+ type=elem.attrib["class"],
+ name=get_elem_value(elem, "name"),
+ package=package,
+ srcs=normalize_paths(get_elem_value(elem, "srcs") or []),
+ hdrs=normalize_paths(get_elem_value(elem, "hdrs") or []),
+ textual_hdrs=normalize_paths(get_elem_value(elem, "textual_hdrs") or []),
+ deps=get_elem_value(elem, "deps") or [],
+ visibility=get_elem_value(elem, "visibility") or [],
+ testonly=get_elem_value(elem, "testonly") or False)
+
+
+def read_build(package):
+ """Runs bazel query on given package file and returns all cc rules."""
+ result = subprocess.check_output(
+ ["bazel", "query", package + ":all", "--output", "xml"])
+ root = xml.etree.ElementTree.fromstring(result)
+ return [
+ parse_rule(elem, package)
+ for elem in root
+ if elem.tag == "rule" and elem.attrib["class"].startswith("cc_")
+ ]
+
+
+def collect_rules(root_path):
+ """Collects and returns all rules from root path recursively."""
+ rules = []
+ for cur, _, _ in os.walk(root_path):
+ build_path = os.path.join(cur, "BUILD.bazel")
+ if os.path.exists(build_path):
+ rules.extend(read_build("//" + cur))
+ return rules
+
+
+def relevant_rule(rule):
+ """Returns true if a given rule is relevant when generating a podspec."""
+ return (
+ # cc_library only (ignore cc_test, cc_binary)
+ rule.type == "cc_library" and
+ # ignore empty rule
+ (rule.hdrs + rule.textual_hdrs + rule.srcs) and
+ # ignore test-only rule
+ not rule.testonly)
+
+
+def get_spec_var(depth):
+ """Returns the name of variable for spec with given depth."""
+ return "s" if depth == 0 else "s{}".format(depth)
+
+
+def get_spec_name(label):
+ """Converts the label of bazel rule to the name of podspec."""
+ assert label.startswith("//absl/"), "{} doesn't start with //absl/".format(
+ label)
+ # e.g. //absl/apple/banana -> abseil/apple/banana
+ return "abseil/" + label[7:]
+
+
+def write_podspec(f, rules, args):
+ """Writes a podspec from given rules and args."""
+ rule_dir = build_rule_directory(rules)["abseil"]
+ # Write root part with given arguments
+ spec = re.sub(r"\$\{(\w+)\}", lambda x: args[x.group(1)],
+ SPEC_TEMPLATE).lstrip()
+ f.write(spec)
+ # Write all target rules
+ write_podspec_map(f, rule_dir, 0)
+ f.write("end\n")
+
+
+def build_rule_directory(rules):
+ """Builds a tree-style rule directory from given rules."""
+ rule_dir = {}
+ for rule in rules:
+ cur = rule_dir
+ for frag in get_spec_name(rule.package).split("/"):
+ cur = cur.setdefault(frag, {})
+ cur[rule.name] = rule
+ return rule_dir
+
+
+def write_podspec_map(f, cur_map, depth):
+ """Writes podspec from rule map recursively."""
+ for key, value in sorted(cur_map.items()):
+ indent = " " * (depth + 1)
+ f.write("{indent}{var0}.subspec '{key}' do |{var1}|\n".format(
+ indent=indent,
+ key=key,
+ var0=get_spec_var(depth),
+ var1=get_spec_var(depth + 1)))
+ if isinstance(value, dict):
+ write_podspec_map(f, value, depth + 1)
+ else:
+ write_podspec_rule(f, value, depth + 1)
+ f.write("{indent}end\n".format(indent=indent))
+
+
+def write_podspec_rule(f, rule, depth):
+ """Writes podspec from given rule."""
+ indent = " " * (depth + 1)
+ spec_var = get_spec_var(depth)
+ # Puts all files in hdrs, textual_hdrs, and srcs into source_files.
+ # Since CocoaPods treats header_files a bit differently from bazel,
+ # this won't generate a header_files field so that all source_files
+ # are considered as header files.
+ srcs = sorted(set(rule.hdrs + rule.textual_hdrs + rule.srcs))
+ write_indented_list(
+ f, "{indent}{var}.source_files = ".format(indent=indent, var=spec_var),
+ srcs)
+ # Writes dependencies of this rule.
+ for dep in sorted(rule.deps):
+ name = get_spec_name(dep.replace(":", "/"))
+ f.write("{indent}{var}.dependency '{dep}'\n".format(
+ indent=indent, var=spec_var, dep=name))
+ # Writes custom specification.
+ custom_spec = CUSTOM_SPEC_MAP.get(rule.package + ":" + rule.name)
+ if custom_spec:
+ for spec in custom_spec:
+ f.write("{indent}{var}.{spec}\n".format(
+ indent=indent, var=spec_var, spec=spec))
+
+
+def write_indented_list(f, leading, values):
+ """Writes leading values in an indented style."""
+ f.write(leading)
+ f.write((",\n" + " " * len(leading)).join("'{}'".format(v) for v in values))
+ f.write("\n")
+
+
+def generate(args):
+ """Generates a podspec file from all BUILD files under absl directory."""
+ rules = filter(relevant_rule, collect_rules("absl"))
+ with open(args.output, "wt") as f:
+ write_podspec(f, rules, vars(args))
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Generates abseil.podspec from BUILD.bazel")
+ parser.add_argument(
+ "-v", "--version", help="The version of podspec", required=True)
+ parser.add_argument(
+ "-t",
+ "--tag",
+ default=None,
+ help="The name of git tag (default: version)")
+ parser.add_argument(
+ "-o",
+ "--output",
+ default="abseil.podspec",
+ help="The name of output file (default: abseil.podspec)")
+ args = parser.parse_args()
+ if args.tag is None:
+ args.tag = args.version
+ generate(args)
+
+
+if __name__ == "__main__":
+ main()