blob: bf430355805ff8d456957ad52c7695826b4fe0fd [file] [edit]
#!/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()