"Fossies" - the Fresh Open Source Software Archive

Member "abseil-cpp-20230802.1/absl/abseil.podspec.gen.py" (18 Sep 2023, 7377 Bytes) of package /linux/misc/abseil-cpp-20230802.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file.

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