| #!/usr/bin/env python3 |
| import argparse |
| import json |
| import subprocess |
| import sys |
| import os |
| |
| root_libs = [ |
| '//absl/algorithm:algorithm', |
| '//absl/cleanup:cleanup', |
| '//absl/container:btree', |
| '//absl/container:flat_hash_map', |
| '//absl/container:flat_hash_set', |
| '//absl/container:node_hash_map', |
| '//absl/container:node_hash_set', |
| '//absl/debugging:failure_signal_handler', |
| '//absl/flags:flag', |
| '//absl/flags:parse', |
| '//absl/functional:bind_front', |
| '//absl/log:absl_check', |
| '//absl/log:absl_log', |
| '//absl/log:check', |
| '//absl/log:die_if_null', |
| '//absl/log:initialize', |
| '//absl/log:log', |
| '//absl/random:bit_gen_ref', |
| '//absl/random:random', |
| '//absl/status:statusor', |
| '//absl/strings:strings', |
| '//absl/synchronization:synchronization', |
| ] |
| |
| # Convert names like //foo/bar:baz to foo_bar_baz. |
| # If the last segment of the path and the target names are the same, |
| # the target name will be dropped, as per bazel convention. |
| # eg: //foo/bar:bar -> foo_bar |
| def bazel_name_to_bp_name(name): |
| parts = name[name.rfind('/')+1:].split(':') |
| if len(parts) == 2 and parts[0] == parts[1]: |
| name = name.removesuffix(':'+parts[1]) |
| name = name[2:].replace('/', '_').replace(':', '_') |
| return name |
| |
| def main(): |
| query = subprocess.check_output(['bazel', 'query', '--output=streamed_jsonproto', 'kind(cc_library, //...)'], text=True) |
| query = query.split('\n') |
| |
| bp = ''' |
| // This Android.bp file was autogenerated by external/absel-cpp/generate_bp.py |
| // DO NOT EDIT |
| |
| package { |
| default_applicable_licenses: ["libabsl_license"], |
| default_visibility: ["//visibility:private"], |
| } |
| |
| license { |
| name: "libabsl_license", |
| visibility: [":__subpackages__"], |
| license_kinds: ["SPDX-license-identifier-Apache-2.0"], |
| license_text: ["LICENSE"], |
| } |
| ''' |
| |
| libs_graph = {} |
| libs = {} |
| alwayslink_libs = {} |
| public_libs = {} |
| |
| for q in query: |
| if not q.strip(): |
| continue |
| q = json.loads(q) |
| name = q['rule']['name'] |
| libs[name] = q |
| libs_graph[name] = [] |
| for attr in q['rule']['attribute']: |
| if attr['name'] == 'deps': |
| libs_graph[name] = attr.get('stringListValue', []) |
| if attr['name'] == 'alwayslink': |
| if attr.get('booleanValue', False): |
| alwayslink_libs[name] = True |
| if attr['name'] == 'visibility': |
| if "//visibility:public" in attr['stringListValue']: |
| public_libs[name] = True |
| |
| queue = [l for l in root_libs] |
| processed = {} |
| |
| while queue: |
| name = queue.pop() |
| if name in processed: |
| continue |
| processed[name] = True |
| lib = libs[name] |
| |
| bp_mod_name = bazel_name_to_bp_name(name) |
| |
| deps = [] |
| srcs = [] |
| hdrs = [] |
| for attr in lib['rule']['attribute']: |
| if attr['name'] == 'deps': |
| deps = attr.get('stringListValue', []) |
| if attr['name'] == 'srcs': |
| srcs = attr.get('stringListValue', []) |
| if attr['name'] == 'hdrs': |
| hdrs.extend(attr.get('stringListValue', [])) |
| if attr['name'] == 'textual_hdrs': |
| hdrs.extend(attr.get('stringListValue', [])) |
| |
| hdrs = list(sorted(set(hdrs))) |
| |
| srcs = [h[2:].replace(':', '/') for h in srcs] |
| hdrs = [h[2:].replace(':', '/') for h in hdrs] |
| |
| # In bazel, you can put headers in the srcs field to mean "private" headers that are not |
| # re-exported. Soong doesn't allow .h files in srcs, or .inc files anywhere. So make this |
| # a little easier for soong by moving all headers to the actual headers property, which |
| # will cause some internal headers to be exported but at least this is still better than |
| # exporting the whole absl folder. The .inc files are also copied to the generated header |
| # directories because sometimes headers #include the .inc files. |
| hdrs.extend([h for h in srcs if h.endswith('.h') or h.endswith('.inc')]) |
| srcs = [s for s in srcs if not s.endswith('.h') and not s.endswith('.inc')] |
| |
| for dep in deps: |
| if dep in alwayslink_libs: |
| # We are actually using whole_static_libs for everything now, so in theory this |
| # error isn't necessary. But we want to use regular static_libs. We only use |
| # whole_static_libs because soong doesn't re-export static_lib dependencies. |
| # (b/123002125#comment3) |
| sys.exit(f'{name} depends on {dep}, which is alwayslink=1. alwayslink isn\'t supported by this script. (would need to use whole_static_libs instead of static_libs)') |
| |
| generated_hdrs_attr = "" |
| if hdrs: |
| header_files_for_bp = ['"' + h + '"' for h in hdrs] |
| # We add an extra my_include_dir/ to not get duplication location errors, as both in and |
| # out are exactly the same paths |
| header_files_for_out = ['"my_include_dir/' + h + '"' for h in hdrs] |
| bp += f''' |
| genrule {{ |
| name: "{bp_mod_name}_hdrs", |
| srcs: [ |
| {',\n'.join(header_files_for_bp)} |
| ], |
| out: [ |
| {',\n'.join(header_files_for_out)} |
| ], |
| export_include_dirs: ["my_include_dir"], |
| cmd: "mkdir -p $(genDir)/my_include_dir $(genDir)/temp && " + |
| "cp --parents $(in) $(genDir)/temp && " + |
| // delete empty folders automatically created by soong |
| "rm -rf $(genDir)/my_include_dir/* && " + |
| "mv $(genDir)/temp/external/abseil-cpp/absl $(genDir)/my_include_dir/ && " + |
| "rm -rf $(genDir)/temp" |
| }} |
| ''' |
| generated_hdrs_attr = f'generated_headers: ["{bp_mod_name}_hdrs"],\n' |
| generated_hdrs_attr += f'export_generated_headers: ["{bp_mod_name}_hdrs"],\n' |
| |
| shared_libs_attr = '' |
| # Hardcode the addition of liblog to log_sink_set. log_sink_set uses a select() in bazel |
| # to add the liblog dep only on android. |
| if name == "//absl/log/internal:log_sink_set": |
| shared_libs_attr = ''' |
| target: { |
| android: { |
| shared_libs: ["liblog"], |
| }, |
| }, |
| ''' |
| |
| visibility_prop = '' |
| if name in public_libs: |
| visibility_prop = 'visibility: ["//visibility:public"],' |
| |
| bp_deps = [bazel_name_to_bp_name(d) for d in deps] |
| bp_deps_for_bp = ['"' + d + '"' for d in bp_deps] |
| src_files_for_bp = ['"' + h + '"' for h in srcs] |
| bp += f''' |
| cc_library_static {{ |
| name: "{bp_mod_name}", |
| {visibility_prop} |
| srcs: [ |
| {',\n'.join(src_files_for_bp)} |
| ], |
| {generated_hdrs_attr} |
| whole_static_libs: [ |
| {',\n'.join(bp_deps_for_bp)} |
| ], |
| export_static_lib_headers: [ |
| {',\n'.join(bp_deps_for_bp)} |
| ], |
| {shared_libs_attr} |
| host_supported: true, |
| vendor_available: true, |
| product_available: true, |
| recovery_available: true, |
| sdk_version: "current", |
| min_sdk_version: "apex_inherit", |
| stl: "libc++", |
| apex_available: [ |
| "//apex_available:platform", |
| "com.android.adservices", |
| "com.android.extservices", |
| "com.android.media.swcodec", |
| "com.android.ondevicepersonalization", |
| ], |
| }} |
| ''' |
| |
| queue.extend(deps) |
| |
| with open('Android.bp', 'w') as f: |
| f.write(bp) |
| subprocess.check_call(['bpfmt', '-w', 'Android.bp']) |
| |
| if __name__ == "__main__": |
| main() |