xref: /aosp_15_r20/external/angle/scripts/generate_android_bp.py (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1#  Copyright The ANGLE Project Authors. All rights reserved.
2#  Use of this source code is governed by a BSD-style license that can be
3#  found in the LICENSE file.
4#
5# Generates an Android.bp file from the json output of 4 'gn desc' commands.
6# Invoked during Skia rolls by roll_aosp.sh. For local testing, see:
7#  scripts/roll_aosp.sh --genAndroidBp
8
9import json
10import sys
11import re
12import os
13import argparse
14import functools
15import collections
16import textwrap
17from typing import List, Tuple
18
19ROOT_TARGETS = [
20    "//:libGLESv2",
21    "//:libGLESv1_CM",
22    "//:libEGL",
23]
24
25END2END_TEST_TARGET = "//src/tests:angle_end2end_tests__library"
26
27# Used only in generated Android.bp file for DMA-BUF-enabled builds on Android.
28# See b/353262025 for details.
29DMA_BUF_TARGET = "//src/libANGLE/renderer/vulkan:angle_android_vulkan_dma_buf"
30
31BLUEPRINT_COMMENT_PROPERTY = '__android_bp_comment'
32
33CURRENT_SDK_VERSION = 'current'
34MIN_SDK_VERSION = '28'
35TARGET_SDK_VERSION = '35'
36STL = 'libc++_static'
37
38ABI_ARM = 'arm'
39ABI_ARM64 = 'arm64'
40ABI_X86 = 'x86'
41ABI_X64 = 'x86_64'
42
43ABI_TARGETS = [ABI_ARM, ABI_ARM64, ABI_X86, ABI_X64]
44
45
46def gn_abi(abi):
47    # gn uses x64, rather than x86_64
48    return 'x64' if abi == ABI_X64 else abi
49
50
51# Makes dict cache-able "by reference" (assumed not to be mutated)
52class BuildInfo(dict):
53
54    def __hash__(self):
55        return 0
56
57    def __eq__(self, other):
58        return self is other
59
60
61def tabs(indent):
62    return ' ' * (indent * 4)
63
64
65def has_child_values(value):
66    # Elements of the blueprint can be pruned if they are empty lists or dictionaries of empty
67    # lists
68    if isinstance(value, list):
69        return len(value) > 0
70    if isinstance(value, dict):
71        for (item, item_value) in value.items():
72            if has_child_values(item_value):
73                return True
74        return False
75
76    # This is a value leaf node
77    return True
78
79
80def write_blueprint_key_value(output, name, value, indent=1):
81    if not has_child_values(value):
82        return
83
84    if isinstance(value, set) or isinstance(value, list):
85        value = list(sorted(set(value)))
86        if name == 'cflags':
87            fix_fortify_source_cflags(value)
88
89    if isinstance(value, list):
90        output.append(tabs(indent) + '%s: [' % name)
91        for item in value:
92            output.append(tabs(indent + 1) + '"%s",' % item)
93        output.append(tabs(indent) + '],')
94        return
95    if isinstance(value, dict):
96        if not value:
97            return
98        output.append(tabs(indent) + '%s: {' % name)
99        for (item, item_value) in value.items():
100            write_blueprint_key_value(output, item, item_value, indent + 1)
101        output.append(tabs(indent) + '},')
102        return
103    if isinstance(value, bool):
104        output.append(tabs(indent) + '%s: %s,' % (name, 'true' if value else 'false'))
105        return
106    output.append(tabs(indent) + '%s: "%s",' % (name, value))
107
108
109def write_blueprint(output, target_type, values):
110    if target_type == 'license':
111        comment = """
112// Added automatically by a large-scale-change that took the approach of
113// 'apply every license found to every target'. While this makes sure we respect
114// every license restriction, it may not be entirely correct.
115//
116// e.g. GPL in an MIT project might only apply to the contrib/ directory.
117//
118// Please consider splitting the single license below into multiple licenses,
119// taking care not to lose any license_kind information, and overriding the
120// default license using the 'licenses: [...]' property on targets as needed.
121//
122// For unused files, consider creating a 'fileGroup' with "//visibility:private"
123// to attach the license to, and including a comment whether the files may be
124// used in the current project.
125// See: http://go/android-license-faq"""
126        output.append(comment)
127
128    output.append('')
129    blueprint_comment = values.get(BLUEPRINT_COMMENT_PROPERTY)
130    if blueprint_comment:
131        for comment_line in textwrap.wrap(blueprint_comment, width=70):
132            output.append('// %s' % comment_line)
133    output.append('%s {' % target_type)
134    for (key, value) in values.items():
135        if key != BLUEPRINT_COMMENT_PROPERTY:
136            write_blueprint_key_value(output, key, value)
137    output.append('}')
138
139
140def gn_target_to_blueprint_target(target, target_info):
141    if 'output_name' in target_info:
142        return target_info['output_name']
143
144    if target_info.get('type') == 'shared_library':
145        # Deduce name from the shared library path
146        # end2end tests lib and libangle_util.so don't have output_name set for the early return above
147        return os.path.splitext(os.path.basename(target_info['outputs'][0]))[0]
148
149    # Split the gn target name (in the form of //gn_file_path:target_name) into gn_file_path and
150    # target_name
151    match = re.match(r"^//([a-zA-Z0-9\-\+_/]*):([a-zA-Z0-9\-\+_.]+)$", target)
152    assert match is not None, target
153
154    gn_file_path = match.group(1)
155    target_name = match.group(2)
156    assert len(target_name) > 0
157
158    # Clean up the gn file path to be a valid blueprint target name.
159    gn_file_path = gn_file_path.replace("/", "_").replace(".", "_").replace("-", "_")
160
161    # Generate a blueprint target name by merging the gn path and target so each target is unique.
162    # Prepend the 'angle' prefix to all targets in the root path (empty gn_file_path).
163    # Skip this step if the target name already starts with 'angle' to avoid target names such as 'angle_angle_common'.
164    root_prefix = "angle"
165    if len(gn_file_path) == 0 and not target_name.startswith(root_prefix):
166        gn_file_path = root_prefix
167
168    # Avoid names such as _angle_common if the gn_file_path is empty.
169    if len(gn_file_path) > 0:
170        gn_file_path += "_"
171
172    return gn_file_path + target_name
173
174
175def remap_gn_path(path):
176    # TODO: pass the gn gen folder as an arg so it is future proof. b/150457277
177    remap_folders = [
178        ('out/Android/gen/angle/', ''),
179        ('out/Android/gen/', ''),
180    ]
181
182    remapped_path = path
183    for (remap_source, remap_dest) in remap_folders:
184        remapped_path = remapped_path.replace(remap_source, remap_dest)
185
186    return remapped_path
187
188
189def gn_path_to_blueprint_path(source):
190    # gn uses '//' to indicate the root directory, blueprint uses the .bp file's location
191    return remap_gn_path(re.sub(r'^//?', '', source))
192
193
194def gn_paths_to_blueprint_paths(paths):
195    rebased_paths = []
196    for path in paths:
197        rebased_paths.append(gn_path_to_blueprint_path(path))
198    return rebased_paths
199
200
201def gn_sources_to_blueprint_sources(sources):
202    # Blueprints only list source files in the sources list. Headers are only referenced though
203    # include paths.
204    file_extension_allowlist = [
205        '.c',
206        '.cc',
207        '.cpp',
208    ]
209
210    rebased_sources = []
211    for source in sources:
212        if os.path.splitext(source)[1] in file_extension_allowlist:
213            rebased_sources.append(gn_path_to_blueprint_path(source))
214    return rebased_sources
215
216
217target_blockist = [
218    '//build/config:shared_library_deps',
219    '//third_party/zlib:zlib',
220    '//third_party/zlib/google:compression_utils_portable',
221
222    # end2end tests: -> platform libgtest, libgmock
223    '//testing/gtest:gtest',
224    '//testing/gmock:gmock',
225    '//third_party/googletest:gtest',
226    '//third_party/googletest:gmock',
227]
228
229third_party_target_allowlist = [
230    '//third_party/abseil-cpp',
231    '//third_party/glslang/src',
232    '//third_party/spirv-cross/src',
233    '//third_party/spirv-headers/src',
234    '//third_party/spirv-tools/src',
235    '//third_party/vulkan-headers/src',
236    '//third_party/vulkan-loader/src',
237    '//third_party/vulkan-tools/src',
238    '//third_party/vulkan-utility-libraries/src',
239    '//third_party/vulkan-validation-layers/src',
240    '//third_party/vulkan_memory_allocator',
241]
242
243include_blocklist = [
244    '//buildtools/third_party/libc++/',
245    '//third_party/libc++/src/',
246    '//out/Android/gen/third_party/glslang/src/include/',
247    '//third_party/zlib/',
248    '//third_party/zlib/google/',
249    # end2end tests: -> platform libgtest, libgmock
250    '//third_party/googletest/custom/',
251    '//third_party/googletest/src/googletest/include/',
252    '//third_party/googletest/src/googlemock/include/',
253]
254
255targets_using_jni = [
256    '//src/tests:native_test_support_android',
257    '//util:angle_util',
258]
259
260
261@functools.lru_cache(maxsize=None)  # .cache() is py3.9 http://b/246559064#comment8
262def gn_deps_to_blueprint_deps(abi, target, build_info):
263    target_info = build_info[abi][target]
264    static_libs = []
265    shared_libs = []
266    defaults = []
267    generated_headers = []
268    header_libs = []
269    if target in targets_using_jni:
270        header_libs.append('jni_headers')
271    if 'deps' not in target_info:
272        return static_libs, defaults
273
274    for dep in target_info['deps']:
275        if dep not in target_blockist and (not dep.startswith('//third_party') or any(
276                dep.startswith(substring) for substring in third_party_target_allowlist)):
277            dep_info = build_info[abi][dep]
278            blueprint_dep_name = gn_target_to_blueprint_target(dep, dep_info)
279
280            # Depending on the dep type, blueprints reference it differently.
281            gn_dep_type = dep_info['type']
282            if gn_dep_type == 'static_library':
283                static_libs.append(blueprint_dep_name)
284            elif gn_dep_type == 'shared_library':
285                shared_libs.append(blueprint_dep_name)
286            elif gn_dep_type == 'source_set' or gn_dep_type == 'group':
287                defaults.append(blueprint_dep_name)
288            elif gn_dep_type == 'action':
289                # ANGLE in Android repo does not depend on
290                # angle_commit_id target to get ANGLE git has. See b/348044346.
291                # Files include angle_commit.h will be able to find this
292                # header file generated by roll_aosp.sh during ANGLE to
293                # Android roll, instead of the version that is generated
294                # by angle_commit_id build target at compile time.
295                if blueprint_dep_name == 'angle_commit_id':
296                    continue
297                generated_headers.append(blueprint_dep_name)
298
299            # Blueprints do not chain linking of static libraries.
300            (child_static_libs, _, _, child_generated_headers,
301             _) = gn_deps_to_blueprint_deps(abi, dep, build_info)
302
303            # Each target needs to link all child static library dependencies.
304            static_libs += child_static_libs
305
306            # Each blueprint target runs genrules in a different output directory unlike GN. If a
307            # target depends on another's genrule, it wont find the outputs. Propogate generated
308            # headers up the dependency stack.
309            generated_headers += child_generated_headers
310        elif dep == '//third_party/zlib/google:compression_utils_portable':
311            # Replace zlib by Android's zlib, compression_utils_portable is the root dependency
312            shared_libs.append('libz')
313            static_libs.extend(['zlib_google_compression_utils_portable', 'cpufeatures'])
314        elif dep == '//testing/gtest:gtest':
315            static_libs.append('libgtest_ndk_c++')
316        elif dep == '//testing/gmock:gmock':
317            static_libs.append('libgmock_ndk')
318
319    return static_libs, shared_libs, defaults, generated_headers, header_libs
320
321
322def gn_libs_to_blueprint_shared_libraries(target_info):
323    lib_blockist = [
324        'android_support',
325        'unwind',
326    ]
327
328    result = []
329    if 'libs' in target_info:
330        for lib in target_info['libs']:
331            if lib not in lib_blockist and not lib.startswith('//'):
332                android_lib = lib if '@' in lib else 'lib' + lib
333                result.append(android_lib)
334    return result
335
336
337def gn_include_dirs_to_blueprint_include_dirs(target_info):
338    result = []
339    if 'include_dirs' in target_info:
340        for include_dir in target_info['include_dirs']:
341            if len(include_dir) > 0 and include_dir not in include_blocklist:
342                result.append(gn_path_to_blueprint_path(include_dir))
343    return result
344
345
346def escape_quotes(string):
347    return string.replace("\"", "\\\"").replace("\'", "\\\'")
348
349
350def gn_cflags_to_blueprint_cflags(target_info):
351    result = []
352
353    # regexs of allowlisted cflags
354    cflag_allowlist = [
355        r'^-Wno-.*$',  # forward cflags that disable warnings
356        r'^-fvisibility.*$',  # forward visibility (https://gcc.gnu.org/wiki/Visibility) flags for better perf on x86
357        r'-mpclmul'  # forward "-mpclmul" (used by zlib)
358    ]
359
360    for cflag_type in ['cflags', 'cflags_c', 'cflags_cc']:
361        if cflag_type in target_info:
362            for cflag in target_info[cflag_type]:
363                for allowlisted_cflag in cflag_allowlist:
364                    if re.search(allowlisted_cflag, cflag):
365                        result.append(cflag)
366
367    if 'defines' in target_info:
368        for define in target_info['defines']:
369            # Don't emit ANGLE's CPU-bits define here, it will be part of the arch-specific
370            # information later
371            result.append('-D%s' % escape_quotes(define))
372
373    return result
374
375
376blueprint_library_target_types = {
377    "static_library": "cc_library_static",
378    "shared_library": "cc_library_shared",
379    "source_set": "cc_defaults",
380    "group": "cc_defaults",
381}
382
383
384def merge_bps(bps_for_abis):
385    common_bp = {}
386    for abi in ABI_TARGETS:
387        for key, values in bps_for_abis[abi].items():
388            if not isinstance(values, list):
389                # Assume everything that's not a list is common to all ABIs
390                common_bp[key] = values
391                continue
392
393            # Find list values that are common to all ABIs
394            values_in_all_abis = set.intersection(
395                *[set(bps_for_abis[abi2].get(key, [])) for abi2 in ABI_TARGETS])
396
397            for value in values:
398                if value in values_in_all_abis or key == 'defaults':  # arch-specific defaults are not supported
399                    common_bp.setdefault(key, [])
400                    common_bp[key].append(value)
401                else:
402                    common_bp.setdefault('arch', {abi3: {} for abi3 in ABI_TARGETS})
403                    abi_specific = common_bp['arch'][abi]
404                    abi_specific.setdefault(key, [])
405                    abi_specific[key].append(value)
406
407    return common_bp
408
409
410def library_target_to_blueprint(target, build_info):
411    bps_for_abis = {}
412    blueprint_type = ""
413    for abi in ABI_TARGETS:
414        if target not in build_info[abi].keys():
415            bps_for_abis[abi] = {}
416            continue
417
418        target_info = build_info[abi][target]
419
420        blueprint_type = blueprint_library_target_types[target_info['type']]
421
422        bp = {'name': gn_target_to_blueprint_target(target, target_info)}
423
424        if 'sources' in target_info:
425            bp['srcs'] = gn_sources_to_blueprint_sources(target_info['sources'])
426
427        (bp['static_libs'], bp['shared_libs'], bp['defaults'], bp['generated_headers'],
428         bp['header_libs']) = gn_deps_to_blueprint_deps(abi, target, build_info)
429        bp['shared_libs'] += gn_libs_to_blueprint_shared_libraries(target_info)
430
431        bp['local_include_dirs'] = gn_include_dirs_to_blueprint_include_dirs(target_info)
432
433        bp['cflags'] = gn_cflags_to_blueprint_cflags(target_info)
434
435        bp['defaults'].append('angle_common_library_cflags')
436
437        bp['sdk_version'] = CURRENT_SDK_VERSION
438
439        bp['stl'] = STL
440        if target in ROOT_TARGETS:
441            bp['defaults'].append('angle_vendor_cc_defaults')
442            bp['defaults'].append('angle_dma_buf_cc_defaults')
443        bps_for_abis[abi] = bp
444
445    common_bp = merge_bps(bps_for_abis)
446
447    return blueprint_type, common_bp
448
449
450def gn_action_args_to_blueprint_args(blueprint_inputs, blueprint_outputs, args):
451    # TODO: pass the gn gen folder as an arg so we know how to get from the gen path to the root
452    # path. b/150457277
453    remap_folders = [
454        # Specific special-cases first, since the other will strip the prefixes.
455        ('gen/third_party/glslang/src/include/glslang/build_info.h', 'glslang/build_info.h'),
456        ('third_party/glslang/src', 'external/angle/third_party/glslang/src'),
457        ('../../', ''),
458        ('gen/', ''),
459    ]
460
461    result_args = []
462    for arg in args:
463        # Attempt to find if this arg is a path to one of the inputs. If it is, use the blueprint
464        # $(location <path>) argument instead so the path gets remapped properly to the location
465        # that the script is run from
466        remapped_path_arg = arg
467        for (remap_source, remap_dest) in remap_folders:
468            remapped_path_arg = remapped_path_arg.replace(remap_source, remap_dest)
469
470        if remapped_path_arg in blueprint_inputs or remapped_path_arg in blueprint_outputs:
471            result_args.append('$(location %s)' % remapped_path_arg)
472        elif os.path.basename(remapped_path_arg) in blueprint_outputs:
473            result_args.append('$(location %s)' % os.path.basename(remapped_path_arg))
474        else:
475            result_args.append(remapped_path_arg)
476
477    return result_args
478
479
480blueprint_gen_types = {
481    "action": "cc_genrule",
482}
483
484
485inputs_blocklist = [
486    '//.git/HEAD',
487]
488
489outputs_remap = {
490    'build_info.h': 'glslang/build_info.h',
491}
492
493
494def is_input_in_tool_files(tool_files, input):
495    return input in tool_files
496
497
498# special handling the {{response_file_name}} args in GN:
499# see https://gn.googlesource.com/gn/+/main/docs/reference.md#var_response_file_contents
500# in GN, if we use response_file_contents, the GN build system will automatically
501# write contents specified in response_file_contents arg into a temporary file
502# identified by {{response_file_name}}. However, Android blueprint does not have
503# the matching machanism. Android blueprint does automatically generate the
504# temporary file and does not recognize '{{response_file_name}}'.
505# To solve the problem:
506# 1) replace the '{{response_file_name}}' in command argument with the new
507# temporary file name.
508# 2) write the content specified in 'response_file_contents' to the new temporary
509# file
510# This function completes step 1) above. It checks if there are
511# '{{response_file_name}}' used in the command arguments. If there are,
512# the function replaces the '{{response_file_name}}' with the new temp file
513# named 'gn_response_file', and returns the new temp file to indicate
514# we need to complete step 2)
515def handle_gn_build_arg_response_file_name(command_arg_list):
516    new_temp_file_name = None
517    updated_args = command_arg_list[:]
518    for index, arg in enumerate(updated_args):
519        if arg == '{{response_file_name}}':
520            new_temp_file_name = '$(genDir)/gn_response_file'
521            updated_args[index] = new_temp_file_name
522    return new_temp_file_name, updated_args
523
524
525def action_target_to_blueprint(abi, target, build_info):
526    target_info = build_info[abi][target]
527    blueprint_type = blueprint_gen_types[target_info['type']]
528
529    bp = {'name': gn_target_to_blueprint_target(target, target_info)}
530
531    # Blueprints use only one 'srcs', merge all gn inputs into one list.
532    gn_inputs = []
533    if 'inputs' in target_info:
534        for input in target_info['inputs']:
535            if input not in inputs_blocklist:
536                gn_inputs.append(input)
537    if 'sources' in target_info:
538        gn_inputs += target_info['sources']
539    # Filter out the 'script' entry since Android.bp doesn't like the duplicate entries
540    if 'script' in target_info:
541        gn_inputs = [
542            input for input in gn_inputs
543            if not is_input_in_tool_files(target_info['script'], input)
544        ]
545
546    bp_srcs = gn_paths_to_blueprint_paths(gn_inputs)
547
548    bp['srcs'] = bp_srcs
549
550    # genrules generate the output right into the 'root' directory. Strip any path before the
551    # file name.
552    bp_outputs = []
553    for gn_output in target_info['outputs']:
554        output = os.path.basename(gn_output)
555        if output in outputs_remap.keys():
556            output = outputs_remap[output]
557        bp_outputs.append(output)
558
559    bp['out'] = bp_outputs
560
561    bp['tool_files'] = [gn_path_to_blueprint_path(target_info['script'])]
562
563    new_temporary_gn_response_file, updated_args = handle_gn_build_arg_response_file_name(
564        target_info['args'])
565
566    if new_temporary_gn_response_file:
567        # add the command 'echo $(in) > $(genDir)/gn_response_file' to
568        # write $response_file_contents into the new_temporary_gn_response_file.
569        cmd = ['echo $(in) >', new_temporary_gn_response_file, '&&', '$(location)'
570              ] + gn_action_args_to_blueprint_args(bp_srcs, bp_outputs, updated_args)
571    else:
572        cmd = ['$(location)'] + gn_action_args_to_blueprint_args(bp_srcs, bp_outputs,
573                                                                 target_info['args'])
574
575    bp['cmd'] = ' '.join(cmd)
576
577    bp['sdk_version'] = CURRENT_SDK_VERSION
578
579    return blueprint_type, bp
580
581
582def gn_target_to_blueprint(target, build_info):
583    for abi in ABI_TARGETS:
584        gn_type = build_info[abi][target]['type']
585        if gn_type in blueprint_library_target_types:
586            return library_target_to_blueprint(target, build_info)
587        elif gn_type in blueprint_gen_types:
588            return action_target_to_blueprint(abi, target, build_info)
589        else:
590            # Target is not used by this ABI
591            continue
592
593
594@functools.lru_cache(maxsize=None)
595def get_gn_target_dependencies(abi, target, build_info):
596    result = collections.OrderedDict()
597    result[target] = 1
598
599    for dep in build_info[abi][target]['deps']:
600        if dep in target_blockist:
601            # Blocklisted dep
602            continue
603        if dep not in build_info[abi]:
604            # No info for this dep, skip it
605            continue
606
607        # Recurse
608        result.update(get_gn_target_dependencies(abi, dep, build_info))
609
610    return result
611
612
613def get_angle_in_vendor_flag_config():
614    blueprint_results = []
615
616    blueprint_results.append(('soong_config_module_type', {
617        'name': 'angle_config_cc_defaults',
618        'module_type': 'cc_defaults',
619        'config_namespace': 'angle',
620        'bool_variables': ['angle_in_vendor',],
621        'properties': [
622            'target.android.relative_install_path',
623            'vendor',
624        ],
625    }))
626
627    blueprint_results.append(('soong_config_bool_variable', {
628        'name': 'angle_in_vendor',
629    }))
630
631    blueprint_results.append((
632        'angle_config_cc_defaults',
633        {
634            'name': 'angle_vendor_cc_defaults',
635            'vendor': False,
636            'target': {
637                'android': {
638                    # Android EGL loader can not load from /system/egl/${LIB}
639                    # path and hence don't set the relative path so that ANGLE
640                    # libraries get built into /system/${LIB}
641                    'relative_install_path': '',
642                },
643            },
644            'soong_config_variables': {
645                'angle_in_vendor': {
646                    'vendor': True,
647                    'target': {
648                        'android': {
649                            'relative_install_path': 'egl',
650                        },
651                    },
652                },
653            },
654        }))
655
656    return blueprint_results
657
658
659def get_angle_android_dma_buf_flag_config(build_info):
660    """
661    Generates a list of Android.bp definitions for angle_android_dma_buf flag.
662    """
663
664    blueprint_results = []
665
666    blueprint_results.append(('soong_config_module_type', {
667        'name': 'angle_dma_buf_config_cc_defaults',
668        'module_type': 'cc_defaults',
669        'config_namespace': 'angle',
670        'bool_variables': ['angle_android_dma_buf'],
671        'properties': ['defaults']
672    }))
673
674    blueprint_results.append(('soong_config_bool_variable', {
675        'name': 'angle_android_dma_buf',
676    }))
677
678    blueprint_results.append(('angle_dma_buf_config_cc_defaults', {
679        BLUEPRINT_COMMENT_PROPERTY:
680            ('Note: this is a no-op for most builds, only applies to products that explicitly '
681             'enable the angle_android_dma_buf config flag. See b/353262025 for details of the '
682             'products that use it.'),
683        'name': 'angle_dma_buf_cc_defaults',
684        'soong_config_variables': {
685            'angle_android_dma_buf': {
686                'defaults': [gn_target_to_blueprint_target(DMA_BUF_TARGET, {})],
687            }
688        },
689    }))
690
691    return blueprint_results
692
693
694# returns list of (blueprint module type, dict with contents)
695def get_blueprint_targets_from_build_info(build_info: BuildInfo) -> List[Tuple[str, dict]]:
696    targets_to_write = collections.OrderedDict()
697    for abi in ABI_TARGETS:
698        for root_target in ROOT_TARGETS + [END2END_TEST_TARGET, DMA_BUF_TARGET]:
699
700            targets_to_write.update(get_gn_target_dependencies(abi, root_target, build_info))
701
702    generated_targets = []
703    for target in reversed(targets_to_write.keys()):
704        # Do not export angle_commit_id target in Android.bp, because the script
705        # src/commit_id.py invoked by this target  can't guarantee to generate a
706        # meaningful ANGLE git hash during compile time, see b/348044346.
707        # The script src/commit_id.py will be invoked by roll_aosp.h during
708        # ANGLE to Android roll time, and the ANGLE git hash will be output in
709        # {AndroidANGLERoot}/angle_commmit.h.
710        if target == '//:angle_commit_id':
711            continue
712        generated_targets.append(gn_target_to_blueprint(target, build_info))
713
714    return generated_targets
715
716
717def handle_angle_non_conformant_extensions_and_versions(
718    generated_targets: List[Tuple[str, dict]],
719    blueprint_targets: List[dict],
720):
721    """Replace the non conformant cflags with a separate cc_defaults.
722
723    The downstream can custom the cflags easier.
724    """
725    non_conform_cflag = '-DANGLE_EXPOSE_NON_CONFORMANT_EXTENSIONS_AND_VERSIONS'
726    non_conform_defaults = 'angle_non_conformant_extensions_and_versions_cflags'
727
728    blueprint_targets.append(('cc_defaults', {
729        'name': non_conform_defaults,
730        'cflags': [non_conform_cflag],
731    }))
732
733    for _, bp in generated_targets:
734        if 'cflags' in bp and non_conform_cflag in bp['cflags']:
735            bp['cflags'] = list(set(bp['cflags']) - {non_conform_cflag})
736            bp['defaults'].append(non_conform_defaults)
737
738
739def fix_fortify_source_cflags(cflags):
740    # search if there is any cflag starts with '-D_FORTIFY_SOURCE'
741    d_fortify_source_flag = [cflag for cflag in cflags if '-D_FORTIFY_SOURCE' in cflag]
742    # Insert -U_FORTIFY_SOURCE before the first -D_FORTIFY_SOURCE flag.
743    # In case a default mode for FORTIFY_SOURCE is predefined for a compiler,
744    # and the -D_FORTIFY_SOURCE mode we set is different from the default mode,
745    # the compiler will warn about "redefining FORTIFY_SOURCE macro".
746    # To fix this compiler warning, we unset the default mode with
747    # -U_FORTIFY_SOURCE before setting the desired FORTIFY_SOURCE mode in our
748    # cflags.
749    # reference:
750    # https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++#tldr-what-compiler-options-should-i-use
751    if d_fortify_source_flag:
752        cflags.insert(cflags.index(d_fortify_source_flag[0]), '-U_FORTIFY_SOURCE')
753
754
755def main():
756    parser = argparse.ArgumentParser(
757        description='Generate Android blueprints from gn descriptions.')
758
759    for abi in ABI_TARGETS:
760        parser.add_argument(
761            '--gn_json_' + gn_abi(abi),
762            help=gn_abi(abi) +
763            ' gn desc file in json format. Generated with \'gn desc <out_dir> --format=json "*"\'.',
764            required=True)
765    parser.add_argument('--output', help='output file (e.g. Android.bp)')
766    args = vars(parser.parse_args())
767
768    infos = {}
769    for abi in ABI_TARGETS:
770        with open(args['gn_json_' + gn_abi(abi)], 'r') as f:
771            infos[abi] = json.load(f)
772
773    build_info = BuildInfo(infos)
774
775    blueprint_targets = []
776
777    blueprint_targets.extend(get_angle_in_vendor_flag_config())
778    blueprint_targets.extend(get_angle_android_dma_buf_flag_config(build_info))
779
780    blueprint_targets.append((
781        'cc_defaults',
782        {
783            'name':
784                'angle_common_library_cflags',
785            'cpp_std':
786                'gnu++17',  # TODO(b/330910097): std::popcount missing from external/libcxx
787            'cflags': [
788                # Chrome and Android use different versions of Clang which support differnt warning options.
789                # Ignore errors about unrecognized warning flags.
790                '-Wno-unknown-warning-option',
791                '-O2',
792                # Override AOSP build flags to match ANGLE's CQ testing and reduce binary size
793                '-fno-unwind-tables',
794                # Disable stack protector to reduce cpu overhead.
795                '-fno-stack-protector',
796            ],
797        }))
798
799    generated_targets = get_blueprint_targets_from_build_info(build_info)
800
801    # Will modify generated_targets and blueprint_targets in-place to handle the
802    # angle_expose_non_conformant_extensions_and_versions gn argument.
803    handle_angle_non_conformant_extensions_and_versions(generated_targets, blueprint_targets)
804
805    # Move cflags that are repeated in each target to cc_defaults
806    all_cflags = [set(bp['cflags']) for _, bp in generated_targets if 'cflags' in bp]
807    all_target_cflags = set.intersection(*all_cflags)
808
809    for _, bp in generated_targets:
810        if 'cflags' in bp:
811            bp['cflags'] = list(set(bp['cflags']) - all_target_cflags)
812            bp['defaults'].append('angle_common_auto_cflags')
813
814    blueprint_targets.append(('cc_defaults', {
815        'name': 'angle_common_auto_cflags',
816        'cflags': list(all_target_cflags),
817    }))
818    blueprint_targets.extend(generated_targets)
819
820    # Add license build rules
821    blueprint_targets.append(('package', {
822        'default_applicable_licenses': ['external_angle_license'],
823    }))
824    blueprint_targets.append(('license', {
825        'name':
826            'external_angle_license',
827        'visibility': [':__subpackages__'],
828        'license_kinds': [
829            'SPDX-license-identifier-Apache-2.0',
830            'SPDX-license-identifier-BSD',
831            'SPDX-license-identifier-GPL',
832            'SPDX-license-identifier-GPL-2.0',
833            'SPDX-license-identifier-GPL-3.0',
834            'SPDX-license-identifier-LGPL',
835            'SPDX-license-identifier-MIT',
836            'SPDX-license-identifier-Zlib',
837            'legacy_unencumbered',
838        ],
839        'license_text': [
840            'LICENSE',
841            'src/common/third_party/xxhash/LICENSE',
842            'src/libANGLE/renderer/vulkan/shaders/src/third_party/ffx_spd/LICENSE',
843            'src/tests/test_utils/third_party/LICENSE',
844            'src/third_party/libXNVCtrl/LICENSE',
845            'src/third_party/volk/LICENSE.md',
846            'third_party/abseil-cpp/LICENSE',
847            'third_party/android_system_sdk/LICENSE',
848            'third_party/bazel/LICENSE',
849            'third_party/colorama/LICENSE',
850            'third_party/proguard/LICENSE',
851            'third_party/r8/LICENSE',
852            'third_party/turbine/LICENSE',
853            'third_party/glslang/LICENSE',
854            'third_party/glslang/src/LICENSE.txt',
855            'third_party/spirv-headers/LICENSE',
856            'third_party/spirv-headers/src/LICENSE',
857            'third_party/spirv-tools/LICENSE',
858            'third_party/spirv-tools/src/LICENSE',
859            'third_party/spirv-tools/src/utils/vscode/src/lsp/LICENSE',
860            'third_party/vulkan-headers/LICENSE.txt',
861            'third_party/vulkan-headers/src/LICENSE.md',
862            'third_party/vulkan_memory_allocator/LICENSE.txt',
863            'tools/flex-bison/third_party/m4sugar/LICENSE',
864            'tools/flex-bison/third_party/skeletons/LICENSE',
865            'util/windows/third_party/StackWalker/LICENSE',
866        ],
867    }))
868
869    # Add APKs with all of the root libraries and permissions xml
870    blueprint_targets.append((
871        'filegroup',
872        {
873            'name':
874                'ANGLE_srcs',
875            # We only need EmptyMainActivity.java since we just need to be able to reply to the intent
876            # android.app.action.ANGLE_FOR_ANDROID to indicate ANGLE is present on the device.
877            # However, the internal branch currently uses these files with patches in that branch.
878            'srcs': [
879                'src/android_system_settings/src/com/android/angle/MainActivity.java',
880                'src/android_system_settings/src/com/android/angle/common/AngleRuleHelper.java',
881                'src/android_system_settings/src/com/android/angle/common/GlobalSettings.java',
882                'src/android_system_settings/src/com/android/angle/common/MainFragment.java',
883                'src/android_system_settings/src/com/android/angle/common/Receiver.java',
884                'src/android_system_settings/src/com/android/angle/common/SearchProvider.java',
885            ],
886        }))
887
888    blueprint_targets.append(('prebuilt_etc', {
889        'name': 'android.software.angle.xml',
890        'src': 'android/android.software.angle.xml',
891        'product_specific': True,
892        'sub_dir': 'permissions',
893    }))
894
895    blueprint_targets.append((
896        'java_defaults',
897        {
898            'name': 'ANGLE_java_defaults',
899            'sdk_version': 'system_current',
900            'target_sdk_version': TARGET_SDK_VERSION,
901            'min_sdk_version': MIN_SDK_VERSION,
902            'compile_multilib': 'both',
903            'use_embedded_native_libs': True,
904            'jni_libs': [
905                # hack: assume ABI_ARM
906                gn_target_to_blueprint_target(target, build_info[ABI_ARM][target])
907                for target in ROOT_TARGETS
908            ],
909            'aaptflags': [
910                '-0 .json',  # Don't compress *.json files
911                "--extra-packages com.android.angle.common",
912            ],
913            'srcs': [':ANGLE_srcs'],
914            'privileged': True,
915            'product_specific': True,
916            'owner': 'google',
917            'required': ['android.software.angle.xml'],
918        }))
919
920    blueprint_targets.append(('android_library', {
921        'name': 'ANGLE_library',
922        'sdk_version': 'system_current',
923        'target_sdk_version': TARGET_SDK_VERSION,
924        'min_sdk_version': MIN_SDK_VERSION,
925        'resource_dirs': ['src/android_system_settings/res',],
926        'asset_dirs': ['src/android_system_settings/assets',],
927        'aaptflags': ['-0 .json',],
928        'manifest': 'src/android_system_settings/src/com/android/angle/AndroidManifest.xml',
929        'static_libs': ['androidx.preference_preference',],
930    }))
931
932    blueprint_targets.append(('android_app', {
933        'name': 'ANGLE',
934        'defaults': ['ANGLE_java_defaults'],
935        'manifest': 'src/android_system_settings/src/com/android/angle/AndroidManifest.xml',
936        'static_libs': ['ANGLE_library'],
937        'optimize': {
938            'enabled': True,
939            'shrink': True,
940            'proguard_compatibility': False,
941        },
942        'asset_dirs': ['src/android_system_settings/assets',],
943    }))
944
945    blueprint_targets.append((
946        'java_defaults',
947        {
948            'name': 'ANGLE_java_settings_defaults',
949            'sdk_version': 'system_current',
950            'target_sdk_version': TARGET_SDK_VERSION,
951            'min_sdk_version': MIN_SDK_VERSION,
952            'compile_multilib': 'both',
953            'use_embedded_native_libs': True,
954            'aaptflags': [
955                '-0 .json',  # Don't compress *.json files
956                "--extra-packages com.android.angle.common",
957            ],
958            'srcs': [':ANGLE_srcs'],
959            'privileged': True,
960            'product_specific': True,
961            'owner': 'google',
962            'required': ['android.software.angle.xml'],
963        }))
964
965    blueprint_targets.append(('android_app', {
966        'name': 'ANGLE_settings',
967        'defaults': ['ANGLE_java_settings_defaults'],
968        'manifest': 'src/android_system_settings/src/com/android/angle/AndroidManifest.xml',
969        'static_libs': ['ANGLE_library'],
970        'optimize': {
971            'enabled': True,
972            'shrink': True,
973            'proguard_compatibility': False,
974        },
975        'asset_dirs': ['src/android_system_settings/assets',],
976    }))
977
978    output = [
979        """// GENERATED FILE - DO NOT EDIT.
980// Generated by %s
981//
982// Copyright 2020 The ANGLE Project Authors. All rights reserved.
983// Use of this source code is governed by a BSD-style license that can be
984// found in the LICENSE file.
985//""" % sys.argv[0]
986    ]
987    for (blueprint_type, blueprint_data) in blueprint_targets:
988        write_blueprint(output, blueprint_type, blueprint_data)
989
990    with open(args['output'], 'w') as f:
991        f.write('\n'.join(output) + '\n')
992
993
994if __name__ == '__main__':
995    sys.exit(main())
996