xref: /aosp_15_r20/external/cronet/components/cronet/gn2bp/gen_android_bp.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1#!/usr/bin/env python3
2# Copyright (C) 2022 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16# This tool translates a collection of BUILD.gn files into a mostly equivalent
17# Android.bp file for the Android Soong build system. The input to the tool is a
18# JSON description of the GN build definition generated with the following
19# command:
20#
21#   gn desc out --format=json --all-toolchains "//*" > desc.json
22#
23# The tool is then given a list of GN labels for which to generate Android.bp
24# build rules. The dependencies for the GN labels are squashed to the generated
25# Android.bp target, except for actions which get their own genrule. Some
26# libraries are also mapped to their Android equivalents -- see |builtin_deps|.
27
28import argparse
29import json
30import logging as log
31import operator
32import os
33import re
34import sys
35import copy
36from pathlib import Path
37
38import gn_utils
39
40ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
41
42CRONET_LICENSE_NAME = "external_cronet_license"
43
44# Default targets to translate to the blueprint file.
45DEFAULT_TARGETS = [
46    "//components/cronet/android:cronet_api_java",
47    '//components/cronet/android:cronet',
48    '//components/cronet/android:cronet_impl_native_java',
49    '//components/cronet/android:cronet_jni_registration_java',
50]
51
52DEFAULT_TESTS = [
53    '//components/cronet/android:cronet_unittests_android__library',
54    '//net:net_unittests__library',
55    '//components/cronet/android:cronet_tests',
56    '//components/cronet/android:cronet',
57    '//components/cronet/android:cronet_javatests',
58    '//components/cronet/android:cronet_jni_registration_java',
59    '//components/cronet/android:cronet_tests_jni_registration_java',
60    '//testing/android/native_test:native_test_java',
61    '//net/android:net_test_support_provider_java',
62    '//net/android:net_tests_java',
63    '//third_party/netty-tcnative:netty-tcnative-so',
64    '//third_party/netty4:netty_all_java',
65]
66
67EXTRAS_ANDROID_BP_FILE = "Android.extras.bp"
68
69CRONET_API_MODULE_NAME = "cronet_aml_api_java"
70
71# All module names are prefixed with this string to avoid collisions.
72module_prefix = 'cronet_aml_'
73
74REMOVE_GEN_JNI_JARJAR_RULES_FILE = "android/tools/remove-gen-jni-jarjar-rules.txt"
75# Shared libraries which are directly translated to Android system equivalents.
76shared_library_allowlist = [
77    'android',
78    'log',
79]
80
81# Include directories that will be removed from all targets.
82local_include_dirs_denylist = [
83    'third_party/zlib/',
84]
85
86# Name of the module which settings such as compiler flags for all other
87# modules.
88defaults_module = module_prefix + 'defaults'
89
90# Location of the project in the Android source tree.
91tree_path = 'external/cronet'
92
93# Path for the protobuf sources in the standalone build.
94buildtools_protobuf_src = '//buildtools/protobuf/src'
95
96# Location of the protobuf src dir in the Android source tree.
97android_protobuf_src = 'external/protobuf/src'
98
99# put all args on a new line for better diffs.
100NEWLINE = ' " +\n         "'
101
102# Compiler flags which are passed through to the blueprint.
103cflag_allowlist = [
104    # needed for zlib:zlib
105    "-mpclmul",
106    # needed for zlib:zlib
107    "-mssse3",
108    # needed for zlib:zlib
109    "-msse3",
110    # needed for zlib:zlib
111    "-msse4.2",
112    # flags to reduce binary size
113    "-O1",
114    "-O2",
115    "-O3",
116    "-Oz",
117    "-g1",
118    "-g2",
119    "-fdata-sections",
120    "-ffunction-sections",
121    "-fvisibility=hidden",
122    "-fvisibility-inlines-hidden",
123    "-fstack-protector",
124    "-mno-outline",
125    "-mno-outline-atomics",
126    "-fno-asynchronous-unwind-tables",
127    "-fno-unwind-tables",
128]
129
130# Linker flags which are passed through to the blueprint.
131ldflag_allowlist = [
132    # flags to reduce binary size
133    "-Wl,--as-needed",
134    "-Wl,--gc-sections",
135    "-Wl,--icf=all",
136]
137
138
139def get_linker_script_ldflag(script_path):
140    return f'-Wl,--script,{tree_path}/{script_path}'
141
142
143# Additional arguments to apply to Android.bp rules.
144additional_args = {
145    'cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen_headers':
146    [('export_include_dirs', {
147        "net/third_party/quiche/src",
148    })],
149    'cronet_aml_net_third_party_quiche_net_quic_test_tools_proto_gen__testing_headers':
150    [('export_include_dirs', {
151        "net/third_party/quiche/src",
152    })],
153    'cronet_aml_third_party_quic_trace_quic_trace_proto_gen__testing_headers':
154    [('export_include_dirs', {
155        "third_party/quic_trace/src",
156    })],
157    # TODO: fix upstream. Both //base:base and
158    # //base/allocator/partition_allocator:partition_alloc do not create a
159    # dependency on gtest despite using gtest_prod.h.
160    'cronet_aml_base_base': [
161        ('header_libs', {
162            'libgtest_prod_headers',
163        }),
164        ('export_header_lib_headers', {
165            'libgtest_prod_headers',
166        }),
167    ],
168    'cronet_aml_base_allocator_partition_allocator_partition_alloc': [
169        ('header_libs', {
170            'libgtest_prod_headers',
171        }),
172    ],
173    # TODO(b/309920629): Remove once upstreamed.
174    'cronet_aml_components_cronet_android_cronet_api_java': [
175        ('srcs', {
176            'components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java',
177            'components/cronet/android/api/src/org/chromium/net/apihelpers/UploadDataProviders.java',
178        }),
179    ],
180    'cronet_aml_components_cronet_android_cronet_api_java__testing': [
181        ('srcs', {
182            'components/cronet/android/api/src/org/chromium/net/UploadDataProviders.java',
183            'components/cronet/android/api/src/org/chromium/net/apihelpers/UploadDataProviders.java',
184        }),
185    ],
186    'cronet_aml_components_cronet_android_cronet_javatests__testing': [
187        # Needed to @SkipPresubmit annotations
188        ('static_libs', {
189            'net-tests-utils',
190        }),
191        # This is necessary because net-tests-utils compiles against private SDK.
192        ('sdk_version', ""),
193    ],
194    'cronet_aml_testing_android_native_test_native_test_java__testing': [
195        ('srcs', {
196            'testing/android/native_test/java/src/org/chromium/native_test/SignalMaskInfo.java',
197        }),
198    ],
199    'cronet_aml_components_cronet_android_cronet__testing': [
200        ('target', ('android_riscv64', {
201            'stem': "libmainlinecronet_riscv64"
202        })),
203        ('comment', """TODO: remove stem for riscv64
204// This is essential as there can't be two different modules
205// with the same output. We usually got away with that because
206// the non-testing Cronet is part of the Tethering APEX and the
207// testing Cronet is not part of the Tethering APEX which made them
208// look like two different outputs from the build system perspective.
209// However, Cronet does not ship to Tethering APEX for RISCV64 which
210// raises the conflict. Once we start shipping Cronet for RISCV64,
211// this can be removed."""),
212    ],
213    'cronet_aml_third_party_netty_tcnative_netty_tcnative_so__testing':
214    [('cflags', {"-Wno-error=pointer-bool-conversion"})],
215    'cronet_aml_third_party_apache_portable_runtime_apr__testing': [
216        ('cflags', {
217            "-Wno-incompatible-pointer-types-discards-qualifiers",
218        })
219    ]
220}
221
222
223def always_disable(module, arch):
224    return None
225
226
227def enable_zlib(module, arch):
228    # Requires crrev/c/4109079
229    if arch == 'common':
230        module.shared_libs.add('libz')
231    else:
232        module.target[arch].shared_libs.add('libz')
233
234
235def enable_boringssl(module, arch):
236    # Do not add boringssl targets to cc_genrules. This happens, because protobuf targets are
237    # originally static_libraries, but later get converted to a cc_genrule.
238    if module.is_genrule(): return
239    # Lets keep statically linking BoringSSL for testing target for now. This should be fixed.
240    if module.name.endswith(gn_utils.TESTING_SUFFIX): return
241    if arch == 'common':
242        shared_libs = module.shared_libs
243    else:
244        shared_libs = module.target[arch].shared_libs
245    shared_libs.add('//external/cronet/third_party/boringssl:libcrypto')
246    shared_libs.add('//external/cronet/third_party/boringssl:libssl')
247
248
249def add_androidx_experimental_java_deps(module, arch):
250    module.libs.add("androidx.annotation_annotation-experimental-nodeps")
251
252
253def add_androidx_annotation_java_deps(module, arch):
254    module.libs.add("androidx.annotation_annotation-nodeps")
255
256
257def add_protobuf_lite_runtime_java_deps(module, arch):
258    module.static_libs.add("libprotobuf-java-lite")
259
260
261def add_androidx_core_java_deps(module, arch):
262    module.libs.add("androidx.core_core-nodeps")
263
264
265def add_jsr305_java_deps(module, arch):
266    module.libs.add("jsr305")
267
268
269def add_errorprone_annotation_java_deps(module, arch):
270    module.libs.add("error_prone_annotations")
271
272
273def add_androidx_collection_java_deps(module, arch):
274    module.libs.add("androidx.collection_collection-nodeps")
275
276
277def add_junit_java_deps(module, arch):
278    module.static_libs.add("junit")
279
280
281def add_truth_java_deps(module, arch):
282    module.static_libs.add("truth")
283
284
285def add_hamcrest_java_deps(module, arch):
286    module.static_libs.add("hamcrest-library")
287    module.static_libs.add("hamcrest")
288
289
290def add_mockito_java_deps(module, arch):
291    module.static_libs.add("mockito")
292
293
294def add_guava_java_deps(module, arch):
295    module.static_libs.add("guava")
296
297
298def add_androidx_junit_java_deps(module, arch):
299    module.static_libs.add("androidx.test.ext.junit")
300
301
302def add_androidx_test_runner_java_deps(module, arch):
303    module.static_libs.add("androidx.test.runner")
304
305def add_androidx_test_rules_java_deps(module, arch):
306    module.static_libs.add("androidx.test.rules")
307
308
309def add_android_test_base_java_deps(module, arch):
310    module.libs.add("android.test.base")
311
312
313def add_accessibility_test_framework_java_deps(module, arch):
314    module.static_libs.add("accessibility-test-framework")
315
316
317def add_espresso_java_deps(module, arch):
318    module.static_libs.add("androidx.test.espresso.contrib")
319
320
321def add_android_test_mock_java_deps(module, arch):
322    module.libs.add("android.test.mock")
323
324
325def add_androidx_multidex_java_deps(module, arch):
326    # Androidx-multidex is disabled on unbundled branches.
327    pass
328
329
330def add_androidx_test_monitor_java_deps(module, arch):
331    module.libs.add("androidx.test.monitor")
332
333
334def add_androidx_ui_automator_java_deps(module, arch):
335    module.static_libs.add("androidx.test.uiautomator_uiautomator")
336
337
338def add_androidx_test_annotation_java_deps(module, arch):
339    module.static_libs.add("androidx.test.rules")
340
341
342def add_androidx_test_core_java_deps(module, arch):
343    module.static_libs.add("androidx.test.core")
344
345
346def add_androidx_activity_activity(module, arch):
347    module.static_libs.add("androidx.activity_activity")
348
349
350def add_androidx_fragment_fragment(module, arch):
351    module.static_libs.add("androidx.fragment_fragment")
352
353
354# Android equivalents for third-party libraries that the upstream project
355# depends on. This will be applied to normal and testing targets.
356_builtin_deps = {
357    '//buildtools/third_party/libunwind:libunwind':
358    always_disable,
359    '//net/data/ssl/chrome_root_store:gen_root_store_inc':
360    always_disable,
361    '//net/tools/root_store_tool:root_store_tool':
362    always_disable,
363    '//third_party/zlib:zlib':
364    enable_zlib,
365    '//third_party/androidx:androidx_annotation_annotation_java':
366    add_androidx_annotation_java_deps,
367    '//third_party/android_deps:protobuf_lite_runtime_java':
368    add_protobuf_lite_runtime_java_deps,
369    '//third_party/androidx:androidx_annotation_annotation_experimental_java':
370    add_androidx_experimental_java_deps,
371    '//third_party/androidx:androidx_core_core_java':
372    add_androidx_core_java_deps,
373    '//third_party/android_deps:com_google_code_findbugs_jsr305_java':
374    add_jsr305_java_deps,
375    '//third_party/android_deps:com_google_errorprone_error_prone_annotations_java':
376    add_errorprone_annotation_java_deps,
377    '//third_party/androidx:androidx_collection_collection_java':
378    add_androidx_collection_java_deps,
379    '//third_party/junit:junit':
380    add_junit_java_deps,
381    '//third_party/google-truth:google_truth_java':
382    add_truth_java_deps,
383    '//third_party/hamcrest:hamcrest_core_java':
384    add_hamcrest_java_deps,
385    '//third_party/mockito:mockito_java':
386    add_mockito_java_deps,
387    '//third_party/android_deps:guava_android_java':
388    add_guava_java_deps,
389    '//third_party/androidx:androidx_test_ext_junit_java':
390    add_androidx_junit_java_deps,
391    '//third_party/androidx:androidx_test_runner_java':
392    add_androidx_test_runner_java_deps,
393    '//third_party/android_sdk:android_test_base_java':
394    add_android_test_base_java_deps,
395    '//third_party/accessibility_test_framework:accessibility_test_framework_java':
396    add_accessibility_test_framework_java_deps,
397    '//third_party/accessibility_test_framework:accessibility_core_java':
398    add_accessibility_test_framework_java_deps,
399    '//third_party/android_deps:espresso_java':
400    add_espresso_java_deps,
401    '//third_party/android_sdk:android_test_mock_java':
402    add_android_test_mock_java_deps,
403    '//third_party/androidx:androidx_multidex_multidex_java':
404    add_androidx_multidex_java_deps,
405    '//third_party/androidx:androidx_test_monitor_java':
406    add_androidx_test_monitor_java_deps,
407    '//third_party/androidx:androidx_test_annotation_java':
408    add_androidx_test_annotation_java_deps,
409    '//third_party/androidx:androidx_test_core_java':
410    add_androidx_test_core_java_deps,
411    '//third_party/androidx:androidx_test_uiautomator_uiautomator_java':
412    add_androidx_ui_automator_java_deps,
413    '//third_party/hamcrest:hamcrest_java':
414    add_hamcrest_java_deps,
415    '//third_party/androidx:androidx_activity_activity_java':
416    add_androidx_activity_activity,
417    '//third_party/androidx:androidx_fragment_fragment_java':
418    add_androidx_fragment_fragment,
419    '//third_party/androidx:androidx_test_rules_java':
420    add_androidx_test_rules_java_deps,
421}
422builtin_deps = {
423    "{}{}".format(key, suffix): value
424    for key, value in _builtin_deps.items()
425    for suffix in ["", gn_utils.TESTING_SUFFIX]
426}
427
428# Same as _builtin_deps but will only apply what is explicitly specified.
429builtin_deps.update({
430    '//third_party/boringssl:boringssl': enable_boringssl,
431    '//third_party/boringssl:boringssl_asm':
432    # Due to FIPS requirements, downstream BoringSSL has a different "shape" than upstream's.
433    # We're guaranteed that if X depends on :boringssl it will also depend on :boringssl_asm.
434    # Hence, always drop :boringssl_asm and handle the translation entirely in :boringssl.
435    always_disable,
436})
437
438# Name of tethering apex module
439tethering_apex = "com.android.tethering"
440
441# Name of cronet api target
442java_api_target_name = "//components/cronet/android:cronet_api_java"
443
444# Visibility set for package default
445package_default_visibility = ":__subpackages__"
446
447# Visibility set for modules used from Connectivity and within external/cronet
448root_modules_visibility = {
449    "//packages/modules/Connectivity:__subpackages__",
450    "//external/cronet:__subpackages__"
451}
452
453# ----------------------------------------------------------------------------
454# End of configuration.
455# ----------------------------------------------------------------------------
456
457
458def write_blueprint_key_value(output, name, value, sort=True):
459    """Writes a Blueprint key-value pair to the output"""
460
461    if isinstance(value, bool):
462        if value:
463            output.append('    %s: true,' % name)
464        else:
465            output.append('    %s: false,' % name)
466        return
467    if not value:
468        return
469    if isinstance(value, set):
470        value = sorted(value)
471    if isinstance(value, list):
472        output.append('    %s: [' % name)
473        for item in sorted(value) if sort else value:
474            output.append('        "%s",' % item)
475        output.append('    ],')
476        return
477    if isinstance(value, Module.Target):
478        value.to_string(output)
479        return
480    if isinstance(value, dict):
481        kv_output = []
482        for k, v in value.items():
483            write_blueprint_key_value(kv_output, k, v)
484
485        output.append('    %s: {' % name)
486        for line in kv_output:
487            output.append('    %s' % line)
488        output.append('    },')
489        return
490    output.append('    %s: "%s",' % (name, value))
491
492
493class Module(object):
494    """A single module (e.g., cc_binary, cc_test) in a blueprint."""
495
496    class Target(object):
497        """A target-scoped part of a module"""
498
499        def __init__(self, name):
500            self.name = name
501            self.srcs = set()
502            self.shared_libs = set()
503            self.static_libs = set()
504            self.whole_static_libs = set()
505            self.header_libs = set()
506            self.cflags = set()
507            self.stl = None
508            self.cppflags = set()
509            self.local_include_dirs = set()
510            self.generated_headers = set()
511            self.export_generated_headers = set()
512            self.ldflags = set()
513            self.compile_multilib = None
514            self.stem = ""
515            if name == 'host':
516                self.compile_multilib = '64'
517
518        def to_string(self, output):
519            nested_out = []
520            self._output_field(nested_out, 'srcs')
521            self._output_field(nested_out, 'shared_libs')
522            self._output_field(nested_out, 'static_libs')
523            self._output_field(nested_out, 'whole_static_libs')
524            self._output_field(nested_out, 'header_libs')
525            self._output_field(nested_out, 'cflags')
526            self._output_field(nested_out, 'stl')
527            self._output_field(nested_out, 'cppflags')
528            self._output_field(nested_out, 'local_include_dirs')
529            self._output_field(nested_out, 'generated_headers')
530            self._output_field(nested_out, 'export_generated_headers')
531            self._output_field(nested_out, 'ldflags')
532            self._output_field(nested_out, 'stem')
533
534            if nested_out:
535                # This is added here to make sure it doesn't add a `host` arch-specific module just for
536                # `compile_multilib` flag.
537                self._output_field(nested_out, 'compile_multilib')
538                output.append('    %s: {' % self.name)
539                for line in nested_out:
540                    output.append('    %s' % line)
541                output.append('    },')
542
543        def _output_field(self, output, name, sort=True):
544            value = getattr(self, name)
545            return write_blueprint_key_value(output, name, value, sort)
546
547    def __init__(self, mod_type, name, gn_target):
548        self.type = mod_type
549        self.gn_target = gn_target
550        self.name = name
551        self.srcs = set()
552        self.comment = 'GN: ' + gn_target
553        self.shared_libs = set()
554        self.static_libs = set()
555        self.whole_static_libs = set()
556        self.tools = set()
557        self.cmd = None
558        self.host_supported = False
559        self.device_supported = True
560        self.init_rc = set()
561        self.out = set()
562        self.export_include_dirs = set()
563        self.generated_headers = set()
564        self.export_generated_headers = set()
565        self.export_static_lib_headers = set()
566        self.export_header_lib_headers = set()
567        self.defaults = set()
568        self.cflags = set()
569        self.include_dirs = set()
570        self.local_include_dirs = set()
571        self.header_libs = set()
572        self.tool_files = set()
573        # target contains a dict of Targets indexed by os_arch.
574        # example: { 'android_x86': Target('android_x86')
575        self.target = dict()
576        self.target['android'] = self.Target('android')
577        self.target['android_x86'] = self.Target('android_x86')
578        self.target['android_x86_64'] = self.Target('android_x86_64')
579        self.target['android_arm'] = self.Target('android_arm')
580        self.target['android_arm64'] = self.Target('android_arm64')
581        self.target['android_riscv64'] = self.Target('android_riscv64')
582        self.target['host'] = self.Target('host')
583        self.target['glibc'] = self.Target('glibc')
584        self.stl = None
585        self.cpp_std = None
586        self.strip = dict()
587        self.data = set()
588        self.apex_available = set()
589        self.min_sdk_version = None
590        self.proto = dict()
591        self.linker_scripts = set()
592        self.ldflags = set()
593        # The genrule_XXX below are properties that must to be propagated back
594        # on the module(s) that depend on the genrule.
595        self.genrule_headers = set()
596        self.genrule_srcs = set()
597        self.genrule_shared_libs = set()
598        self.genrule_header_libs = set()
599        self.version_script = None
600        self.test_suites = set()
601        self.test_config = None
602        self.cppflags = set()
603        self.rtti = False
604        # Name of the output. Used for setting .so file name for libcronet
605        self.libs = set()
606        self.stem = None
607        self.compile_multilib = None
608        self.aidl = dict()
609        self.plugins = set()
610        self.processor_class = None
611        self.sdk_version = None
612        self.javacflags = set()
613        self.c_std = None
614        self.default_applicable_licenses = set()
615        self.default_visibility = []
616        self.visibility = set()
617        self.gn_type = None
618        self.jarjar_rules = ""
619        self.jars = set()
620
621    def to_string(self, output):
622        if self.comment:
623            output.append('// %s' % self.comment)
624        output.append('%s {' % self.type)
625        self._output_field(output, 'name')
626        self._output_field(output, 'srcs')
627        self._output_field(output, 'shared_libs')
628        self._output_field(output, 'static_libs')
629        self._output_field(output, 'whole_static_libs')
630        self._output_field(output, 'tools')
631        self._output_field(output, 'cmd', sort=False)
632        if self.host_supported:
633            self._output_field(output, 'host_supported')
634        if not self.device_supported:
635            self._output_field(output, 'device_supported')
636        self._output_field(output, 'init_rc')
637        self._output_field(output, 'out')
638        self._output_field(output, 'export_include_dirs')
639        self._output_field(output, 'generated_headers')
640        self._output_field(output, 'export_generated_headers')
641        self._output_field(output, 'export_static_lib_headers')
642        self._output_field(output, 'export_header_lib_headers')
643        self._output_field(output, 'defaults')
644        self._output_field(output, 'cflags')
645        self._output_field(output, 'include_dirs')
646        self._output_field(output, 'local_include_dirs')
647        self._output_field(output, 'header_libs')
648        self._output_field(output, 'strip')
649        self._output_field(output, 'tool_files')
650        self._output_field(output, 'data')
651        self._output_field(output, 'stl')
652        self._output_field(output, 'cpp_std')
653        self._output_field(output, 'apex_available')
654        self._output_field(output, 'min_sdk_version')
655        self._output_field(output, 'version_script')
656        self._output_field(output, 'test_suites')
657        self._output_field(output, 'test_config')
658        self._output_field(output, 'proto')
659        self._output_field(output, 'linker_scripts')
660        self._output_field(output, 'ldflags')
661        self._output_field(output, 'cppflags')
662        self._output_field(output, 'libs')
663        self._output_field(output, 'stem')
664        self._output_field(output, 'compile_multilib')
665        self._output_field(output, 'aidl')
666        self._output_field(output, 'plugins')
667        self._output_field(output, 'processor_class')
668        self._output_field(output, 'sdk_version')
669        self._output_field(output, 'javacflags')
670        self._output_field(output, 'c_std')
671        self._output_field(output, 'default_applicable_licenses')
672        self._output_field(output, 'default_visibility')
673        self._output_field(output, 'visibility')
674        self._output_field(output, 'jarjar_rules')
675        self._output_field(output, 'jars')
676        if self.rtti:
677            self._output_field(output, 'rtti')
678
679        target_out = []
680        for arch, target in sorted(self.target.items()):
681            # _output_field calls getattr(self, arch).
682            setattr(self, arch, target)
683            self._output_field(target_out, arch)
684
685        if target_out:
686            output.append('    target: {')
687            for line in target_out:
688                output.append('    %s' % line)
689            output.append('    },')
690
691        output.append('}')
692        output.append('')
693
694    def add_android_shared_lib(self, lib):
695        if self.type == 'cc_binary_host':
696            raise Exception(
697                'Adding Android shared lib for host tool is unsupported')
698        elif self.host_supported:
699            self.target['android'].shared_libs.add(lib)
700        else:
701            self.shared_libs.add(lib)
702
703    def is_test(self):
704        if gn_utils.TESTING_SUFFIX in self.name:
705            name_without_prefix = self.name[:self.name.find(gn_utils.
706                                                            TESTING_SUFFIX)]
707            return any([
708                name_without_prefix == label_to_module_name(target)
709                for target in DEFAULT_TESTS
710            ])
711        return False
712
713    def _output_field(self, output, name, sort=True):
714        value = getattr(self, name)
715        return write_blueprint_key_value(output, name, value, sort)
716
717    def is_compiled(self):
718        return self.type not in ('cc_genrule', 'filegroup', 'java_genrule')
719
720    def is_genrule(self):
721        return self.type == "cc_genrule"
722
723    def has_input_files(self):
724        if self.type in ["java_library", "java_import"]:
725            return True
726        if len(self.srcs) > 0:
727            return True
728        if any([len(target.srcs) > 0 for target in self.target.values()]):
729            return True
730        # Allow cc_static_library with export_generated_headers as those are crucial for
731        # the depending modules
732        return len(self.export_generated_headers) > 0
733
734
735class Blueprint(object):
736    """In-memory representation of an Android.bp file."""
737
738    def __init__(self):
739        self.modules = {}
740
741    def add_module(self, module):
742        """Adds a new module to the blueprint, replacing any existing module
743        with the same name.
744
745        Args:
746            module: Module instance.
747        """
748        self.modules[module.name] = module
749
750    def to_string(self, output):
751        for m in sorted(self.modules.values(), key=lambda m: m.name):
752            if m.type != "cc_library_static" or m.has_input_files():
753                # Don't print cc_library_static with empty srcs. These attributes are already
754                # propagated up the tree. Printing them messes the presubmits because
755                # every module is compiled while those targets are not reachable in
756                # a normal compilation path.
757                m.to_string(output)
758
759
760def label_to_module_name(label):
761    """Turn a GN label (e.g., //:perfetto_tests) into a module name."""
762    module = re.sub(r'^//:?', '', label)
763    module = re.sub(r'[^a-zA-Z0-9_]', '_', module)
764
765    if not module.startswith(module_prefix):
766        return module_prefix + module
767    return module
768
769
770def is_supported_source_file(name):
771    """Returns True if |name| can appear in a 'srcs' list."""
772    return os.path.splitext(name)[1] in [
773        '.c', '.cc', '.cpp', '.java', '.proto', '.S', '.aidl'
774    ]
775
776
777def get_protoc_module_name(gn):
778    protoc_gn_target_name = gn.get_target('//third_party/protobuf:protoc').name
779    return label_to_module_name(protoc_gn_target_name)
780
781
782def create_proto_modules(blueprint, gn, target):
783    """Generate genrules for a proto GN target.
784
785    GN actions are used to dynamically generate files during the build. The
786    Soong equivalent is a genrule. This function turns a specific kind of
787    genrule which turns .proto files into source and header files into a pair
788    equivalent genrules.
789
790    Args:
791        blueprint: Blueprint instance which is being generated.
792        target: gn_utils.Target object.
793
794    Returns:
795        The source_genrule module.
796    """
797    assert (target.type == 'proto_library')
798
799    protoc_module_name = get_protoc_module_name(gn)
800    tools = {protoc_module_name}
801    cpp_out_dir = '$(genDir)/%s/%s/' % (tree_path, target.proto_in_dir)
802    target_module_name = label_to_module_name(target.name)
803
804    # In GN builds the proto path is always relative to the output directory
805    # (out/tmp.xxx).
806    cmd = ['$(location %s)' % protoc_module_name]
807    cmd += ['--proto_path=%s/%s' % (tree_path, target.proto_in_dir)]
808
809    for proto_path in target.proto_paths:
810        cmd += [f'--proto_path={tree_path}/{proto_path}']
811    if buildtools_protobuf_src in target.proto_paths:
812        cmd += ['--proto_path=%s' % android_protobuf_src]
813
814    # We don't generate any targets for source_set proto modules because
815    # they will be inlined into other modules if required.
816    if target.proto_plugin == 'source_set':
817        return None
818
819    # Descriptor targets only generate a single target.
820    if target.proto_plugin == 'descriptor':
821        out = '{}.bin'.format(target_module_name)
822
823        cmd += ['--descriptor_set_out=$(out)']
824        cmd += ['$(in)']
825
826        descriptor_module = Module('cc_genrule', target_module_name,
827                                   target.name)
828        descriptor_module.cmd = ' '.join(cmd)
829        descriptor_module.out = [out]
830        descriptor_module.tools = tools
831        blueprint.add_module(descriptor_module)
832
833        # Recursively extract the .proto files of all the dependencies and
834        # add them to srcs.
835        descriptor_module.srcs.update(
836            gn_utils.label_to_path(src) for src in target.sources)
837        for dep in target.proto_deps:
838            current_target = gn.get_target(dep)
839            descriptor_module.srcs.update(
840                gn_utils.label_to_path(src) for src in current_target.sources)
841
842        return descriptor_module
843
844    # We create two genrules for each proto target: one for the headers and
845    # another for the sources. This is because the module that depends on the
846    # generated files needs to declare two different types of dependencies --
847    # source files in 'srcs' and headers in 'generated_headers' -- and it's not
848    # valid to generate .h files from a source dependency and vice versa.
849    source_module_name = target_module_name
850    source_module = Module('cc_genrule', source_module_name, target.name)
851    blueprint.add_module(source_module)
852    source_module.srcs.update(
853        gn_utils.label_to_path(src) for src in target.sources)
854
855    header_module = Module('cc_genrule', source_module_name + '_headers',
856                           target.name)
857    blueprint.add_module(header_module)
858    header_module.srcs = set(source_module.srcs)
859
860    # TODO(primiano): at some point we should remove this. This was introduced
861    # by aosp/1108421 when adding "protos/" to .proto include paths, in order to
862    # avoid doing multi-repo changes and allow old clients in the android tree
863    # to still do the old #include "perfetto/..." rather than
864    # #include "protos/perfetto/...".
865    header_module.export_include_dirs = {'.', 'protos'}
866    # Since the .cc file and .h get created by a different gerule target, they
867    # are not put in the same intermediate path, so local includes do not work
868    # without explictily exporting the include dir.
869    header_module.export_include_dirs.add(target.proto_in_dir)
870
871    # This function does not return header_module so setting apex_available attribute here.
872    header_module.apex_available.add(tethering_apex)
873
874    source_module.genrule_srcs.add(':' + source_module.name)
875    source_module.genrule_headers.add(header_module.name)
876
877    if target.proto_plugin == 'proto':
878        suffixes = ['pb']
879        source_module.genrule_shared_libs.add('libprotobuf-cpp-lite')
880        cmd += ['--cpp_out=lite=true:' + cpp_out_dir]
881    else:
882        raise Exception('Unsupported proto plugin: %s' % target.proto_plugin)
883
884    cmd += ['$(in)']
885    source_module.cmd = ' '.join(cmd)
886    header_module.cmd = source_module.cmd
887    source_module.tools = tools
888    header_module.tools = tools
889
890    for sfx in suffixes:
891        source_module.out.update(
892            '%s/%s' % (tree_path, src.replace('.proto', '.%s.cc' % sfx))
893            for src in source_module.srcs)
894        header_module.out.update(
895            '%s/%s' % (tree_path, src.replace('.proto', '.%s.h' % sfx))
896            for src in header_module.srcs)
897    # This has proto files that will be used for reference resolution
898    # but not compiled into cpp files. These additional sources has no output.
899    proto_data_sources = sorted([
900        gn_utils.label_to_path(proto_src) for proto_src in target.inputs
901        if proto_src.endswith(".proto")
902    ])
903    source_module.srcs.update(proto_data_sources)
904    header_module.srcs.update(proto_data_sources)
905    return source_module
906
907
908def create_gcc_preprocess_modules(blueprint, target):
909    # gcc_preprocess.py internally execute host gcc which is not allowed in genrule.
910    # So, this function create multiple modules and realize equivalent processing
911    # TODO: Consider to support gcc_preprocess.py in different way
912    # It's not great to have genrule and cc_object in the dependency from java_library
913    assert (len(target.sources) == 1)
914    source = list(target.sources)[0]
915    assert (Path(source).suffix == '.template')
916    stem = Path(source).stem
917
918    bp_module_name = label_to_module_name(target.name)
919
920    # Rename .template to .cc since cc_object does not accept .template file as srcs
921    rename_module = Module('genrule', bp_module_name + '_rename', target.name)
922    rename_module.srcs.add(gn_utils.label_to_path(source))
923    rename_module.out.add(stem + '.cc')
924    rename_module.cmd = 'cp $(in) $(out)'
925    blueprint.add_module(rename_module)
926
927    # Preprocess template file and generates java file
928    preprocess_module = Module('cc_object', bp_module_name + '_preprocess',
929                               target.name)
930    # -E: stop after preprocessing.
931    # -P: disable line markers, i.e. '#line 309'
932    preprocess_module.cflags.update(['-E', '-P', '-DANDROID'])
933    preprocess_module.srcs.add(':' + rename_module.name)
934    defines = [
935        '-D' + target.args[i + 1] for i, arg in enumerate(target.args)
936        if arg == '--define'
937    ]
938    preprocess_module.cflags.update(defines)
939    # HACK: Specifying compile_multilib to build cc_object only once.
940    # Without this, soong complain to genrule that depends on cc_object when built for 64bit target.
941    # It seems this is because cc object is a module with per-architecture variants and genrule is a
942    # module with default variant. For 64bit target, cc_object is built multiple times for 32/64bit
943    # modes and genrule doesn't know which one to depend on.
944    preprocess_module.compile_multilib = 'first'
945    blueprint.add_module(preprocess_module)
946
947    # Generates srcjar using soong_zip
948    module = Module('genrule', bp_module_name, target.name)
949    module.srcs.add(':' + preprocess_module.name)
950    module.out.add(stem + '.srcjar')
951    module.cmd = NEWLINE.join([
952        f'cp $(in) $(genDir)/{stem}.java &&',
953        f'$(location soong_zip) -o $(out) -srcjar -C $(genDir) -f $(genDir)/{stem}.java'
954    ])
955    module.tools.add('soong_zip')
956    blueprint.add_module(module)
957    return module
958
959
960class BaseActionSanitizer():
961
962    def __init__(self, target, arch):
963        # Just to be on the safe side, create a deep-copy.
964        self.target = copy.deepcopy(target)
965        if arch:
966            # Merge arch specific attributes
967            self.target.sources |= arch.sources
968            self.target.inputs |= arch.inputs
969            self.target.outputs |= arch.outputs
970            self.target.script = self.target.script or arch.script
971            self.target.args = self.target.args or arch.args
972            self.target.response_file_contents = \
973              self.target.response_file_contents or arch.response_file_contents
974        self.target.args = self._normalize_args()
975
976    def get_name(self):
977        return label_to_module_name(self.target.name)
978
979    def _normalize_args(self):
980        # Convert ['--param=value'] to ['--param', 'value'] for consistency.
981        # Escape quotations.
982        normalized_args = []
983        for arg in self.target.args:
984            arg = arg.replace('"', r'\"')
985            if arg.startswith('-'):
986                normalized_args.extend(arg.split('='))
987            else:
988                normalized_args.append(arg)
989        return normalized_args
990
991    # There are three types of args:
992    # - flags (--flag)
993    # - value args (--arg value)
994    # - list args (--arg value1 --arg value2)
995    # value args have exactly one arg value pair and list args have one or more arg value pairs.
996    # Note that the set of list args contains the set of value args.
997    # This is because list and value args are identical when the list args has only one arg value pair
998    # Some functions provide special implementations for each type, while others
999    # work on all of them.
1000    def _has_arg(self, arg):
1001        return arg in self.target.args
1002
1003    def _get_arg_indices(self, target_arg):
1004        return [
1005            i for i, arg in enumerate(self.target.args) if arg == target_arg
1006        ]
1007
1008    # Whether an arg value pair appears once or more times
1009    def _is_list_arg(self, arg):
1010        indices = self._get_arg_indices(arg)
1011        return len(indices) > 0 and all(
1012            [not self.target.args[i + 1].startswith('--') for i in indices])
1013
1014    def _update_list_arg(self, arg, func, throw_if_absent=True):
1015        if self._should_fail_silently(arg, throw_if_absent):
1016            return
1017        assert (self._is_list_arg(arg))
1018        indices = self._get_arg_indices(arg)
1019        for i in indices:
1020            self._set_arg_at(i + 1, func(self.target.args[i + 1]))
1021
1022    # Whether an arg value pair appears exactly once
1023    def _is_value_arg(self, arg):
1024        return operator.countOf(self.target.args,
1025                                arg) == 1 and self._is_list_arg(arg)
1026
1027    def _get_value_arg(self, arg):
1028        assert (self._is_value_arg(arg))
1029        i = self.target.args.index(arg)
1030        return self.target.args[i + 1]
1031
1032    # used to check whether a function call should cause an error when an arg is
1033    # missing.
1034    def _should_fail_silently(self, arg, throw_if_absent):
1035        return not throw_if_absent and not self._has_arg(arg)
1036
1037    def _set_value_arg(self, arg, value, throw_if_absent=True):
1038        if self._should_fail_silently(arg, throw_if_absent):
1039            return
1040        assert (self._is_value_arg(arg))
1041        i = self.target.args.index(arg)
1042        self.target.args[i + 1] = value
1043
1044    def _update_value_arg(self, arg, func, throw_if_absent=True):
1045        if self._should_fail_silently(arg, throw_if_absent):
1046            return
1047        self._set_value_arg(arg, func(self._get_value_arg(arg)))
1048
1049    def _set_arg_at(self, position, value):
1050        self.target.args[position] = value
1051
1052    def _update_arg_at(self, position, func):
1053        self.target.args[position] = func(self.target.args[position])
1054
1055    def _delete_value_arg(self, arg, throw_if_absent=True):
1056        if self._should_fail_silently(arg, throw_if_absent):
1057            return
1058        assert (self._is_value_arg(arg))
1059        i = self.target.args.index(arg)
1060        self.target.args.pop(i)
1061        self.target.args.pop(i)
1062
1063    def _append_arg(self, arg, value):
1064        self.target.args.append(arg)
1065        self.target.args.append(value)
1066
1067    def _sanitize_filepath_with_location_tag(self, arg):
1068        if arg.startswith('../../'):
1069            arg = self._sanitize_filepath(arg)
1070            arg = self._add_location_tag(arg)
1071        return arg
1072
1073    # wrap filename in location tag.
1074    def _add_location_tag(self, filename):
1075        return '$(location %s)' % filename
1076
1077    # applies common directory transformation that *should* be universally applicable.
1078    # TODO: verify if it actually *is* universally applicable.
1079    def _sanitize_filepath(self, filepath):
1080        # Careful, order matters!
1081        # delete all leading ../
1082        filepath = re.sub('^(\.\./)+', '', filepath)
1083        filepath = re.sub('^gen/jni_headers', '$(genDir)', filepath)
1084        filepath = re.sub('^gen', '$(genDir)', filepath)
1085        return filepath
1086
1087    # Iterate through all the args and apply function
1088    def _update_all_args(self, func):
1089        self.target.args = [func(arg) for arg in self.target.args]
1090
1091    def get_pre_cmd(self):
1092        pre_cmd = []
1093        out_dirs = [
1094            out[:out.rfind("/")] for out in self.target.outputs if "/" in out
1095        ]
1096        # Sort the list to make the output deterministic.
1097        for out_dir in sorted(set(out_dirs)):
1098            pre_cmd.append("mkdir -p $(genDir)/{} && ".format(out_dir))
1099        return NEWLINE.join(pre_cmd)
1100
1101    def get_base_cmd(self):
1102        arg_string = NEWLINE.join(self.target.args)
1103        cmd = '$(location %s) %s' % (gn_utils.label_to_path(
1104            self.target.script), arg_string)
1105
1106        if self.use_response_file:
1107            # Pipe response file contents into script
1108            cmd = 'echo \'%s\' |%s%s' % (self.target.response_file_contents,
1109                                         NEWLINE, cmd)
1110        return cmd
1111
1112    def get_cmd(self):
1113        return self.get_pre_cmd() + self.get_base_cmd()
1114
1115    def get_outputs(self):
1116        return self.target.outputs
1117
1118    def get_srcs(self):
1119        # gn treats inputs and sources for actions equally.
1120        # soong only supports source files inside srcs, non-source files are added as
1121        # tool_files dependency.
1122        files = self.target.sources.union(self.target.inputs)
1123        return {
1124            gn_utils.label_to_path(file)
1125            for file in files if is_supported_source_file(file)
1126        }
1127
1128    def get_tools(self):
1129        return set()
1130
1131    def get_tool_files(self):
1132        # gn treats inputs and sources for actions equally.
1133        # soong only supports source files inside srcs, non-source files are added as
1134        # tool_files dependency.
1135        files = self.target.sources.union(self.target.inputs)
1136        tool_files = {
1137            gn_utils.label_to_path(file)
1138            for file in files if not is_supported_source_file(file)
1139        }
1140        tool_files.add(gn_utils.label_to_path(self.target.script))
1141        return tool_files
1142
1143    def _sanitize_args(self):
1144        # Handle passing parameters via response file by piping them into the script
1145        # and reading them from /dev/stdin.
1146
1147        self.use_response_file = gn_utils.RESPONSE_FILE in self.target.args
1148        if self.use_response_file:
1149            # Replace {{response_file_contents}} with /dev/stdin
1150            self.target.args = [
1151                '/dev/stdin' if it == gn_utils.RESPONSE_FILE else it
1152                for it in self.target.args
1153            ]
1154
1155    def _sanitize_inputs(self):
1156        pass
1157
1158    def get_deps(self):
1159        return self.target.deps
1160
1161    def sanitize(self):
1162        self._sanitize_args()
1163        self._sanitize_inputs()
1164
1165    # Whether this target generates header files
1166    def is_header_generated(self):
1167        return any(
1168            os.path.splitext(it)[1] == '.h' for it in self.target.outputs)
1169
1170
1171class WriteBuildDateHeaderSanitizer(BaseActionSanitizer):
1172
1173    def _sanitize_args(self):
1174        self._set_arg_at(0, '$(out)')
1175        super()._sanitize_args()
1176
1177
1178class WriteBuildFlagHeaderSanitizer(BaseActionSanitizer):
1179
1180    def _sanitize_args(self):
1181        self._set_value_arg('--gen-dir', '.')
1182        self._set_value_arg('--output', '$(out)')
1183        super()._sanitize_args()
1184
1185
1186class GnRunBinarySanitizer(BaseActionSanitizer):
1187
1188    def __init__(self, target, arch):
1189        super().__init__(target, arch)
1190        self.binary_to_target = {
1191            "clang_x64/transport_security_state_generator":
1192            "cronet_aml_net_tools_transport_security_state_generator_transport_security_state_generator__testing",
1193        }
1194        self.binary = self.binary_to_target[self.target.args[0]]
1195
1196    def _replace_gen_with_location_tag(self, arg):
1197        if arg.startswith("gen/"):
1198            return "$(location %s)" % arg.replace("gen/", "")
1199        return arg
1200
1201    def _replace_binary(self, arg):
1202        if arg in self.binary_to_target:
1203            return '$(location %s)' % self.binary
1204        return arg
1205
1206    def _remove_python_args(self):
1207        self.target.args = [
1208            arg for arg in self.target.args if "python3" not in arg
1209        ]
1210
1211    def _sanitize_args(self):
1212        self._update_all_args(self._sanitize_filepath_with_location_tag)
1213        self._update_all_args(self._replace_gen_with_location_tag)
1214        self._update_all_args(self._replace_binary)
1215        self._remove_python_args()
1216        super()._sanitize_args()
1217
1218    def get_tools(self):
1219        tools = super().get_tools()
1220        tools.add(self.binary)
1221        return tools
1222
1223    def get_cmd(self):
1224        # Remove the script and use the binary right away
1225        return self.get_pre_cmd() + NEWLINE.join(self.target.args)
1226
1227
1228class JniGeneratorSanitizer(BaseActionSanitizer):
1229
1230    def __init__(self, target, arch, is_test_target):
1231        self.is_test_target = is_test_target
1232        super().__init__(target, arch)
1233
1234    def get_srcs(self):
1235        all_srcs = super().get_srcs()
1236        all_srcs.update({
1237            gn_utils.label_to_path(file)
1238            for file in self.target.transitive_jni_java_sources
1239            if is_supported_source_file(file)
1240        })
1241        return set(src for src in all_srcs if src.endswith(".java"))
1242
1243    def _add_location_tag_to_filepath(self, arg):
1244        if not arg.endswith('.class'):
1245            # --input_file supports both .class specifiers or source files as arguments.
1246            # Only source files need to be wrapped inside a $(location <label>) tag.
1247            arg = self._add_location_tag(arg)
1248        return arg
1249
1250    def _sanitize_args(self):
1251        self._set_value_arg('--jar-file', '$(location :current_android_jar)',
1252                            False)
1253        if self._has_arg('--jar-file'):
1254            self._set_value_arg('--javap', '$(location :javap)')
1255        self._update_value_arg('--srcjar-path', self._sanitize_filepath, False)
1256        self._update_value_arg('--output-dir', self._sanitize_filepath)
1257        self._update_value_arg('--extra-include', self._sanitize_filepath,
1258                               False)
1259        self._update_list_arg('--input-file', self._sanitize_filepath)
1260        self._update_list_arg('--input-file',
1261                              self._add_location_tag_to_filepath)
1262        if not self.is_test_target and not self._has_arg('--jar-file'):
1263            # Don't jarjar classes that already exists within the java SDK. The headers generated
1264            # from those genrule can simply call into the original class as it exists outside
1265            # of cronet's jar.
1266            # Only jarjar platform code
1267            self._append_arg('--package-prefix', 'android.net.connectivity')
1268        super()._sanitize_args()
1269
1270    def get_outputs(self):
1271        # fix target.output directory to match #include statements.
1272        return {
1273            re.sub('^jni_headers/', '', out)
1274            for out in super().get_outputs()
1275        }
1276
1277    def get_tool_files(self):
1278        tool_files = super().get_tool_files()
1279
1280        # Filter android.jar and add :current_android_jar
1281        tool_files = {
1282            file
1283            if not file.endswith('android.jar') else ':current_android_jar'
1284            for file in tool_files
1285        }
1286        # Filter bin/javap
1287        tool_files = {
1288            file
1289            for file in tool_files if not file.endswith('bin/javap')
1290        }
1291        return tool_files
1292
1293    def get_tools(self):
1294        tools = super().get_tools()
1295        if self._has_arg('--jar-file'):
1296            tools.add(":javap")
1297        return tools
1298
1299
1300class JavaJniGeneratorSanitizer(JniGeneratorSanitizer):
1301
1302    def __init__(self, target, arch, is_test_target):
1303        self.is_test_target = is_test_target
1304        super().__init__(target, arch, is_test_target)
1305
1306    def get_outputs(self):
1307        # fix target.output directory to match #include statements.
1308        outputs = {
1309            re.sub('^jni_headers/', '', out)
1310            for out in super().get_outputs()
1311        }
1312        self.target.outputs = [
1313            out for out in outputs if out.endswith(".srcjar")
1314        ]
1315        return outputs
1316
1317    def get_deps(self):
1318        return {}
1319
1320    def get_name(self):
1321        name = super().get_name() + "__java"
1322        return name
1323
1324
1325class JniRegistrationGeneratorSanitizer(BaseActionSanitizer):
1326
1327    def __init__(self, target, arch, is_test_target):
1328        self.is_test_target = is_test_target
1329        super().__init__(target, arch)
1330
1331    def get_srcs(self):
1332        all_srcs = super().get_srcs()
1333        all_srcs.update({
1334            gn_utils.label_to_path(file)
1335            for file in self.target.transitive_jni_java_sources
1336            if is_supported_source_file(file)
1337        })
1338        return set(src for src in all_srcs if src.endswith(".java"))
1339
1340    def _sanitize_inputs(self):
1341        self.target.inputs = [
1342            file for file in self.target.inputs
1343            if not file.startswith('//out/')
1344        ]
1345
1346    def get_outputs(self):
1347        return {
1348            re.sub('^jni_headers/', '', out)
1349            for out in super().get_outputs()
1350        }
1351
1352    def _sanitize_args(self):
1353        self._update_value_arg('--depfile', self._sanitize_filepath)
1354        self._update_value_arg('--srcjar-path', self._sanitize_filepath)
1355        self._update_value_arg('--header-path', self._sanitize_filepath)
1356        self._delete_value_arg('--depfile', False)
1357        self._set_value_arg('--java-sources-file', '$(genDir)/java.sources')
1358        if not self.is_test_target:
1359            # Only jarjar platform code
1360            self._append_arg('--package-prefix', 'android.net.connectivity')
1361        super()._sanitize_args()
1362
1363    def get_cmd(self):
1364        # jni_registration_generator.py doesn't work with python2
1365        cmd = "python3 " + super().get_base_cmd()
1366        # Path in the original sources file does not work in genrule.
1367        # So creating sources file in cmd based on the srcs of this target.
1368        # Adding ../$(current_dir)/ to the head because jni_registration_generator.py uses the files
1369        # whose path startswith(..)
1370        commands = [
1371            "current_dir=`basename \\\`pwd\\\``;", "for f in $(in);", "do",
1372            "echo \\\"../$$current_dir/$$f\\\" >> $(genDir)/java.sources;",
1373            "done;", cmd
1374        ]
1375
1376        return self.get_pre_cmd() + NEWLINE.join(commands)
1377
1378
1379class JavaJniRegistrationGeneratorSanitizer(JniRegistrationGeneratorSanitizer):
1380
1381    def get_name(self):
1382        name = super().get_name() + "__java"
1383        return name
1384
1385    def get_outputs(self):
1386        return [
1387            out for out in super().get_outputs() if out.endswith(".srcjar")
1388        ]
1389
1390    def get_deps(self):
1391        return {}
1392
1393
1394class VersionSanitizer(BaseActionSanitizer):
1395
1396    def _sanitize_args(self):
1397        self._set_value_arg('-o', '$(out)')
1398        # args for the version.py contain file path without leading --arg key. So apply sanitize
1399        # function for all the args.
1400        self._update_all_args(self._sanitize_filepath_with_location_tag)
1401        for (i, arg) in enumerate(self.target.args):
1402            if arg == '-e':
1403                self.target.args[i + 1] = "'%s'" % self.target.args[i + 1]
1404        super()._sanitize_args()
1405
1406    def get_tool_files(self):
1407        tool_files = super().get_tool_files()
1408        # android_chrome_version.py is not specified in anywhere but version.py imports this file
1409        tool_files.add('build/util/android_chrome_version.py')
1410        return tool_files
1411
1412
1413class JavaCppEnumSanitizer(BaseActionSanitizer):
1414
1415    def _sanitize_args(self):
1416        self._update_all_args(self._sanitize_filepath_with_location_tag)
1417        self._set_value_arg('--srcjar', '$(out)')
1418        super()._sanitize_args()
1419
1420
1421class MakeDafsaSanitizer(BaseActionSanitizer):
1422
1423    def is_header_generated(self):
1424        # This script generates .cc files but they are #included by other sources
1425        # (e.g. registry_controlled_domain.cc)
1426        return True
1427
1428
1429class JavaCppFeatureSanitizer(BaseActionSanitizer):
1430
1431    def _sanitize_args(self):
1432        self._update_all_args(self._sanitize_filepath_with_location_tag)
1433        self._set_value_arg('--srcjar', '$(out)')
1434        super()._sanitize_args()
1435
1436
1437class JavaCppStringSanitizer(BaseActionSanitizer):
1438
1439    def _sanitize_args(self):
1440        self._update_all_args(self._sanitize_filepath_with_location_tag)
1441        self._set_value_arg('--srcjar', '$(out)')
1442        super()._sanitize_args()
1443
1444
1445class WriteNativeLibrariesJavaSanitizer(BaseActionSanitizer):
1446
1447    def _sanitize_args(self):
1448        self._set_value_arg('--output', '$(out)')
1449        super()._sanitize_args()
1450
1451
1452class ProtocJavaSanitizer(BaseActionSanitizer):
1453
1454    def __init__(self, target, arch, gn):
1455        super().__init__(target, arch)
1456        self._protoc = get_protoc_module_name(gn)
1457
1458    def _sanitize_proto_path(self, arg):
1459        arg = self._sanitize_filepath(arg)
1460        return tree_path + '/' + arg
1461
1462    def _sanitize_args(self):
1463        super()._sanitize_args()
1464        self._delete_value_arg('--depfile')
1465        self._set_value_arg('--protoc', '$(location %s)' % self._protoc)
1466        self._update_value_arg('--proto-path', self._sanitize_proto_path)
1467        self._set_value_arg('--srcjar', '$(out)')
1468        self._update_arg_at(-1, self._sanitize_filepath_with_location_tag)
1469
1470    def get_tools(self):
1471        tools = super().get_tools()
1472        tools.add(self._protoc)
1473        return tools
1474
1475
1476def get_action_sanitizer(gn, target, type, arch, is_test_target):
1477    if target.script == "//build/write_buildflag_header.py":
1478        return WriteBuildFlagHeaderSanitizer(target, arch)
1479    elif target.script == "//base/write_build_date_header.py":
1480        return WriteBuildDateHeaderSanitizer(target, arch)
1481    elif target.script == "//build/util/version.py":
1482        return VersionSanitizer(target, arch)
1483    elif target.script == "//build/android/gyp/java_cpp_enum.py":
1484        return JavaCppEnumSanitizer(target, arch)
1485    elif target.script == "//net/tools/dafsa/make_dafsa.py":
1486        return MakeDafsaSanitizer(target, arch)
1487    elif target.script == '//build/android/gyp/java_cpp_features.py':
1488        return JavaCppFeatureSanitizer(target, arch)
1489    elif target.script == '//build/android/gyp/java_cpp_strings.py':
1490        return JavaCppStringSanitizer(target, arch)
1491    elif target.script == '//build/android/gyp/write_native_libraries_java.py':
1492        return WriteNativeLibrariesJavaSanitizer(target, arch)
1493    elif target.script == '//build/gn_run_binary.py':
1494        return GnRunBinarySanitizer(target, arch)
1495    elif target.script == '//build/protoc_java.py':
1496        return ProtocJavaSanitizer(target, arch, gn)
1497    elif target.script == '//third_party/jni_zero/jni_zero.py':
1498        if target.args[0] == 'generate-final':
1499            if type == 'java_genrule':
1500                # Fill up the sources of the target for JniRegistrationGenerator
1501                # actions with all the java sources found under targets of type
1502                # `generate_jni`. Note 1: Only do this for the java part in order to
1503                # generate a complete GEN_JNI. The C++ part MUST only include java
1504                # source files that are listed explicitly in `generate_jni` targets
1505                # in the transitive dependency, this is handled inside the action
1506                # sanitizer itself (See `get_srcs`). Adding java sources that are not
1507                # listed to the C++ version of JniRegistrationGenerator will result
1508                # in undefined symbols as the C++ part generates declarations that
1509                # would have no definitions. Note 2: This is only done for the
1510                # testing targets because their JniRegistration is not complete,
1511                # Chromium generates Jni files for testing targets implicitly (See
1512                # https://source.chromium.org/chromium/chromium/src/+/main:testing
1513                # /test.gni;l=422;bpv=1;bpt=0;drc
1514                # =02820c1b362c3a00f426d7c4eab18703d89cda03) to avoid having to
1515                # replicate the same setup, just fill up the java JniRegistration
1516                # with all  java sources found under `generate_jni` targets and fill
1517                # the C++ version with the exact files.
1518                if is_test_target:
1519                    target.sources.update(gn.jni_java_sources)
1520                return JavaJniRegistrationGeneratorSanitizer(
1521                    target, arch, is_test_target)
1522            else:
1523                return JniRegistrationGeneratorSanitizer(
1524                    target, arch, is_test_target)
1525        else:
1526            if type == 'cc_genrule':
1527                return JniGeneratorSanitizer(target, arch, is_test_target)
1528            else:
1529                return JavaJniGeneratorSanitizer(target, arch, is_test_target)
1530    else:
1531        raise Exception('Unsupported action %s from %s' %
1532                        (target.script, target.name))
1533
1534
1535def create_action_foreach_modules(blueprint, gn, target, is_test_target):
1536    """ The following assumes that rebase_path exists in the args.
1537  The args of an action_foreach contains hints about which output files are generated
1538  by which source files.
1539  This is copied directly from the args
1540  "gen/net/base/registry_controlled_domains/{{source_name_part}}-reversed-inc.cc"
1541  So each source file will generate an output whose name is the {source_name-reversed-inc.cc}
1542  """
1543    new_args = []
1544    for i, src in enumerate(sorted(target.sources)):
1545        # don't add script arg for the first source -- create_action_module
1546        # already does this.
1547        if i != 0:
1548            new_args.append('&&')
1549            new_args.append('python3 $(location %s)' %
1550                            gn_utils.label_to_path(target.script))
1551        for arg in target.args:
1552            if '{{source}}' in arg:
1553                new_args.append('$(location %s)' %
1554                                (gn_utils.label_to_path(src)))
1555            elif '{{source_name_part}}' in arg:
1556                source_name_part = src.split("/")[-1]  # Get the file name only
1557                source_name_part = source_name_part.split(".")[
1558                    0]  # Remove the extension (Ex: .cc)
1559                file_name = arg.replace('{{source_name_part}}',
1560                                        source_name_part).split("/")[-1]
1561                # file_name represent the output file name. But we need the whole path
1562                # This can be found from target.outputs.
1563                for out in target.outputs:
1564                    if out.endswith(file_name):
1565                        new_args.append('$(location %s)' % out)
1566
1567                for file in (target.sources | target.inputs):
1568                    if file.endswith(file_name):
1569                        new_args.append('$(location %s)' %
1570                                        gn_utils.label_to_path(file))
1571            else:
1572                new_args.append(arg)
1573
1574    target.args = new_args
1575    return create_action_module(blueprint, gn, target, 'cc_genrule',
1576                                is_test_target)
1577
1578
1579def create_action_module_internal(gn,
1580                                  target,
1581                                  type,
1582                                  is_test_target,
1583                                  blueprint,
1584                                  arch=None):
1585    if target.script == '//build/android/gyp/gcc_preprocess.py':
1586        return create_gcc_preprocess_modules(blueprint, target)
1587    sanitizer = get_action_sanitizer(gn, target, type, arch, is_test_target)
1588    sanitizer.sanitize()
1589
1590    module = Module(type, sanitizer.get_name(), target.name)
1591    module.cmd = sanitizer.get_cmd()
1592    module.out = sanitizer.get_outputs()
1593    if sanitizer.is_header_generated():
1594        module.genrule_headers.add(module.name)
1595    module.srcs = sanitizer.get_srcs()
1596    module.tool_files = sanitizer.get_tool_files()
1597    module.tools = sanitizer.get_tools()
1598    target.deps = sanitizer.get_deps()
1599
1600    return module
1601
1602
1603def get_cmd_condition(arch):
1604    '''
1605  :param arch: archtecture name e.g. android_x86_64, android_arm64
1606  :return: condition that can be used in cc_genrule cmd to switch the behavior based on arch
1607  '''
1608    if arch == "android_x86_64":
1609        return "( $$CC_ARCH == 'x86_64' && $$CC_OS == 'android' )"
1610    elif arch == "android_x86":
1611        return "( $$CC_ARCH == 'x86' && $$CC_OS == 'android' )"
1612    elif arch == "android_arm":
1613        return "( $$CC_ARCH == 'arm' && $$CC_OS == 'android' )"
1614    elif arch == "android_arm64":
1615        return "( $$CC_ARCH == 'arm64' && $$CC_OS == 'android' )"
1616    elif arch == "host":
1617        return "$$CC_OS != 'android'"
1618    else:
1619        raise Exception(f'Unknown architecture type {arch}')
1620
1621
1622def merge_cmd(modules, genrule_type):
1623    '''
1624  :param modules: dictionary whose key is arch name and value is module
1625  :param genrule_type: cc_genrule or java_genrule
1626  :return: merged command or common command if all the archs have the same command.
1627  '''
1628    commands = list({module.cmd for module in modules.values()})
1629    if len(commands) == 1:
1630        # If all the archs have the same command, return the command
1631        return commands[0]
1632
1633    if genrule_type != 'cc_genrule':
1634        raise Exception(
1635            f'{genrule_type} can not have different cmd between archs')
1636
1637    merged_cmd = []
1638    for arch, module in modules.items():
1639        merged_cmd.append(f'if [[ {get_cmd_condition(arch)} ]];')
1640        merged_cmd.append('then')
1641        merged_cmd.append(module.cmd + ';')
1642        merged_cmd.append('fi;')
1643    return NEWLINE.join(merged_cmd)
1644
1645
1646def merge_modules(modules, genrule_type):
1647    '''
1648  :param modules: dictionary whose key is arch name and value is module
1649  :param genrule_type: cc_genrule or java_genrule
1650  :return: merged module of input modules
1651  '''
1652    merged_module = list(modules.values())[0]
1653
1654    # Following attributes must be the same between archs
1655    for key in ('out', 'genrule_headers', 'srcs', 'tool_files'):
1656        if any([
1657                getattr(merged_module, key) != getattr(module, key)
1658                for module in modules.values()
1659        ]):
1660            raise Exception(
1661                f'{merged_module.name} has different values for {key} between archs'
1662            )
1663
1664    merged_module.cmd = merge_cmd(modules, genrule_type)
1665    return merged_module
1666
1667
1668def create_action_module(blueprint, gn, target, genrule_type, is_test_target):
1669    '''
1670  Create module for action target and add to the blueprint. If target has arch specific attributes
1671  this function merge them and create a single module.
1672  :param blueprint:
1673  :param target: target which is converted to the module.
1674  :param genrule_type: cc_genrule or java_genrule
1675  :return: created module
1676  '''
1677    # TODO: Handle this target correctly, this target generates java_genrule but this target has
1678    # different value for cpu-family arg between archs
1679    if re.match('//build/android:native_libraries_gen(__testing)?$',
1680                target.name):
1681        module = create_action_module_internal(gn, target, genrule_type,
1682                                               is_test_target, blueprint,
1683                                               target.arch['android_arm'])
1684        blueprint.add_module(module)
1685        return module
1686
1687    modules = {
1688        arch_name:
1689        create_action_module_internal(gn, target, genrule_type, is_test_target,
1690                                      blueprint, arch)
1691        for arch_name, arch in target.get_archs().items()
1692    }
1693    module = merge_modules(modules, genrule_type)
1694    blueprint.add_module(module)
1695    return module
1696
1697
1698def _get_cflags(cflags, defines):
1699    cflags = {flag for flag in cflags if flag in cflag_allowlist}
1700    # Consider proper allowlist or denylist if needed
1701    cflags |= set("-D%s" % define.replace("\"", "\\\"") for define in defines)
1702    return cflags
1703
1704
1705def _set_linker_script(module, libs):
1706    for lib in libs:
1707        if lib.endswith(".lds"):
1708            module.ldflags.add(
1709                get_linker_script_ldflag(gn_utils.label_to_path(lib)))
1710
1711
1712def set_module_flags(module, module_type, cflags, defines, ldflags, libs):
1713    module.cflags.update(_get_cflags(cflags, defines))
1714    module.ldflags.update({
1715        flag
1716        for flag in ldflags
1717        if flag in ldflag_allowlist or flag.startswith("-Wl,-wrap,")
1718    })
1719    _set_linker_script(module, libs)
1720    # TODO: implement proper cflag parsing.
1721    for flag in cflags:
1722        if '-std=' in flag:
1723            module.cpp_std = flag[len('-std='):]
1724        if '-fexceptions' in flag:
1725            module.cppflags.add('-fexceptions')
1726
1727
1728def set_module_include_dirs(module, cflags, include_dirs):
1729    for flag in cflags:
1730        if '-isystem' in flag:
1731            module.local_include_dirs.add(flag[len('-isystem../../'):])
1732
1733    # Adding local_include_dirs is necessary due to source_sets / filegroups
1734    # which do not properly propagate include directories.
1735    # Filter any directory inside //out as a) this directory does not exist for
1736    # aosp / soong builds and b) the include directory should already be
1737    # configured via library dependency.
1738    module.local_include_dirs.update([
1739        gn_utils.label_to_path(d) for d in include_dirs
1740        if not d.startswith('//out')
1741    ])
1742    # Remove prohibited include directories
1743    module.local_include_dirs = [
1744        d for d in module.local_include_dirs
1745        if d not in local_include_dirs_denylist
1746    ]
1747
1748
1749def create_modules_from_target(blueprint, gn, gn_target_name,
1750                               is_descendant_of_java, is_test_target):
1751    """Generate module(s) for a given GN target.
1752
1753    Given a GN target name, generate one or more corresponding modules into a
1754    blueprint. The only case when this generates >1 module is proto libraries.
1755
1756    Args:
1757        blueprint: Blueprint instance which is being generated.
1758        gn: gn_utils.GnParser object.
1759        gn_target_name: GN target for module generation.
1760    """
1761    bp_module_name = label_to_module_name(gn_target_name)
1762    target = gn.get_target(gn_target_name)
1763    is_descendant_of_java = is_descendant_of_java or target.type == "java_library"
1764
1765    # Append __java suffix to actions reachable from java_library. This is necessary
1766    # to differentiate them from cc actions.
1767    # This means that a GN action of name X will be translated to two different modules of names
1768    # X and X__java(only if X is reachable from a java target).
1769    if target.type == "action" and is_descendant_of_java:
1770        bp_module_name += "__java"
1771
1772    if bp_module_name in blueprint.modules:
1773        return blueprint.modules[bp_module_name]
1774
1775    log.info('create modules for %s (%s)', target.name, target.type)
1776
1777    if target.type == 'executable':
1778        if target.testonly:
1779            module_type = 'cc_test'
1780        else:
1781            # Can be used for both host and device targets.
1782            module_type = 'cc_binary'
1783        module = Module(module_type, bp_module_name, gn_target_name)
1784    elif target.type in ['static_library', 'source_set']:
1785        module = Module('cc_library_static', bp_module_name, gn_target_name)
1786    elif target.type == 'shared_library':
1787        module = Module('cc_library_shared', bp_module_name, gn_target_name)
1788    elif target.type == 'group':
1789        # "group" targets are resolved recursively by gn_utils.get_target().
1790        # There's nothing we need to do at this level for them.
1791        return None
1792    elif target.type == 'proto_library':
1793        module = create_proto_modules(blueprint, gn, target)
1794        if module is None:
1795            return None
1796    elif target.type == 'action':
1797        module = create_action_module(
1798            blueprint, gn, target,
1799            'java_genrule' if is_descendant_of_java else 'cc_genrule',
1800            is_test_target)
1801    elif target.type == 'action_foreach':
1802        module = create_action_foreach_modules(blueprint, gn, target,
1803                                               is_test_target)
1804    elif target.type == 'copy':
1805        # TODO: careful now! copy targets are not supported yet, but this will stop
1806        # traversing the dependency tree. For //base:base, this is not a big
1807        # problem as libicu contains the only copy target which happens to be a
1808        # leaf node.
1809        return None
1810    elif target.type == 'java_library':
1811        if target.jar_path:
1812            module = Module('java_import', bp_module_name, gn_target_name)
1813            module.jars.add(target.jar_path)
1814        else:
1815            module = Module('java_library', bp_module_name, gn_target_name)
1816            # Don't remove GEN_JNI from those modules as they have the real GEN_JNI that we want to include
1817            if gn_target_name not in [
1818                    '//components/cronet/android:cronet_jni_registration_java',
1819                    '//components/cronet/android:cronet_jni_registration_java__testing',
1820                    '//components/cronet/android:cronet_tests_jni_registration_java__testing'
1821            ]:
1822                module.jarjar_rules = REMOVE_GEN_JNI_JARJAR_RULES_FILE
1823            module.min_sdk_version = 30
1824            module.apex_available = [tethering_apex]
1825    else:
1826        raise Exception('Unknown target %s (%s)' % (target.name, target.type))
1827
1828    blueprint.add_module(module)
1829    if target.type not in ['action', 'action_foreach']:
1830        # Actions should get their srcs from their corresponding ActionSanitizer as actionSanitizer
1831        # filters srcs differently according to the type of the action.
1832        module.srcs.update(
1833            gn_utils.label_to_path(src) for src in target.sources
1834            if is_supported_source_file(src))
1835
1836    # Add arch-specific properties
1837    for arch_name, arch in target.get_archs().items():
1838        module.target[arch_name].srcs.update(
1839            gn_utils.label_to_path(src) for src in arch.sources
1840            if is_supported_source_file(src))
1841
1842    module.rtti = target.rtti
1843
1844    if target.type in gn_utils.LINKER_UNIT_TYPES:
1845        set_module_flags(module, module.type, target.cflags, target.defines,
1846                         target.ldflags, target.libs)
1847        set_module_include_dirs(module, target.cflags, target.include_dirs)
1848        # TODO: set_module_xxx is confusing, apply similar function to module and target in better way.
1849        for arch_name, arch in target.get_archs().items():
1850            # TODO(aymanm): Make libs arch-specific.
1851            set_module_flags(module.target[arch_name], module.type,
1852                             arch.cflags, arch.defines, arch.ldflags, [])
1853            # -Xclang -target-feature -Xclang +mte are used to enable MTE (Memory Tagging Extensions).
1854            # Flags which does not start with '-' could not be in the cflags so enabling MTE by
1855            # -march and -mcpu Feature Modifiers. MTE is only available on arm64. This is needed for
1856            # building //base/allocator/partition_allocator:partition_alloc for arm64.
1857            if '+mte' in arch.cflags and arch_name == 'android_arm64':
1858                module.target[arch_name].cflags.add('-march=armv8-a+memtag')
1859            set_module_include_dirs(module.target[arch_name], arch.cflags,
1860                                    arch.include_dirs)
1861
1862    module.host_supported = target.host_supported()
1863    module.device_supported = target.device_supported()
1864    module.gn_type = target.type
1865
1866    if module.is_genrule():
1867        module.apex_available.add(tethering_apex)
1868
1869    if module.type == "java_library":
1870        if gn_utils.contains_aidl(target.sources):
1871            # frameworks/base/core/java includes the source files that are used to compile framework.aidl.
1872            # framework.aidl is added implicitly as a dependency to every AIDL GN action, this can be
1873            # identified by third_party/android_sdk/public/platforms/android-34/framework.aidl.
1874            module.aidl["include_dirs"] = {"frameworks/base/core/java/"}
1875            module.aidl["local_include_dirs"] = target.local_aidl_includes
1876        # This is used to compile against the public SDK APIs.
1877        # See build/soong/android/sdk_version.go for more information on different SDK versions.
1878        module.sdk_version = "current"
1879
1880    if module.is_compiled():
1881        # Don't try to inject library/source dependencies into genrules or
1882        # filegroups because they are not compiled in the traditional sense.
1883        if module.type != "java_library":
1884            module.defaults = [defaults_module]
1885        for lib in target.libs:
1886            # Generally library names should be mangled as 'libXXX', unless they
1887            # are HAL libraries (e.g., [email protected]) or AIDL c++ / NDK
1888            # libraries (e.g. "android.hardware.power.stats-V1-cpp")
1889            android_lib = lib if '@' in lib or "-cpp" in lib or "-ndk" in lib \
1890              else 'lib' + lib
1891            if lib in shared_library_allowlist:
1892                module.add_android_shared_lib(android_lib)
1893
1894    # If the module is a static library, export all the generated headers.
1895    if module.type == 'cc_library_static':
1896        module.export_generated_headers = module.generated_headers
1897
1898    if module.name in [
1899            'cronet_aml_components_cronet_android_cronet',
1900            'cronet_aml_components_cronet_android_cronet' +
1901            gn_utils.TESTING_SUFFIX
1902    ]:
1903        if target.output_name is None:
1904            raise Exception('Failed to get output_name for libcronet name')
1905        # .so file name needs to match with CronetLibraryLoader.java (e.g. libcronet.109.0.5386.0.so)
1906        # So setting the output name based on the output_name from the desc.json
1907        module.stem = 'libmainline' + target.output_name
1908    elif module.is_test() and module.type == 'cc_library_shared':
1909        if target.output_name:
1910            # If we have an output name already declared, use it.
1911            module.stem = 'lib' + target.output_name
1912        else:
1913            # Tests output should be a shared library in the format of 'lib[module_name]'
1914            module.stem = 'lib' + target.get_target_name(
1915            )[:target.get_target_name().find(gn_utils.TESTING_SUFFIX)]
1916
1917    # dep_name is an unmangled GN target name (e.g. //foo:bar(toolchain)).
1918    all_deps = [(dep_name, 'common') for dep_name in target.proto_deps]
1919    for arch_name, arch in target.arch.items():
1920        all_deps += [(dep_name, arch_name) for dep_name in arch.deps]
1921
1922    # Sort deps before iteration to make result deterministic.
1923    for (dep_name, arch_name) in sorted(all_deps):
1924        module_target = module.target[
1925            arch_name] if arch_name != 'common' else module
1926        # |builtin_deps| override GN deps with Android-specific ones. See the
1927        # config in the top of this file.
1928        if dep_name in builtin_deps:
1929            builtin_deps[dep_name](module, arch_name)
1930            continue
1931
1932        dep_module = create_modules_from_target(blueprint, gn, dep_name,
1933                                                is_descendant_of_java,
1934                                                is_test_target)
1935
1936        if dep_module is None:
1937            continue
1938        # TODO: Proper dependency check for genrule.
1939        # Currently, only propagating genrule dependencies.
1940        # Also, currently, all the dependencies are propagated upwards.
1941        # in gn, public_deps should be propagated but deps should not.
1942        # Not sure this information is available in the desc.json.
1943        # Following rule works for adding android_runtime_jni_headers to base:base.
1944        # If this doesn't work for other target, hardcoding for specific target
1945        # might be better.
1946        if module.is_genrule() and dep_module.is_genrule():
1947            if module_target.gn_type != "proto_library":
1948                # proto_library are treated differently because each proto action
1949                # is split into two different targets, a cpp target and a header target.
1950                # the cpp target is used as the entry point to the proto action, hence
1951                # it should not be propagated as a genrule header because it generates
1952                # cpp files only.
1953                module_target.genrule_headers.add(dep_module.name)
1954            module_target.genrule_headers.update(dep_module.genrule_headers)
1955
1956        # For filegroups, and genrule, recurse but don't apply the
1957        # deps.
1958        if not module.is_compiled() or module.is_genrule():
1959            continue
1960
1961        # Drop compiled modules that doesn't provide any benefit. This is mostly
1962        # applicable to source_sets when converted to cc_static_library, sometimes
1963        # the source set only has header files which are dropped so the module becomes empty.
1964        # is_compiled is there to prevent dropping of genrules.
1965        if dep_module.is_compiled() and not dep_module.has_input_files():
1966            continue
1967
1968        if dep_module.type == 'cc_library_shared':
1969            module_target.shared_libs.add(dep_module.name)
1970        elif dep_module.type == 'cc_library_static':
1971            if module.type in ['cc_library_shared', 'cc_binary'
1972                               ] and dep_module.type == 'cc_library_static':
1973                module_target.whole_static_libs.add(dep_module.name)
1974            else:
1975                module_target.generated_headers.update(
1976                    dep_module.generated_headers)
1977                module_target.shared_libs.update(dep_module.shared_libs)
1978                module_target.header_libs.update(dep_module.header_libs)
1979        elif dep_module.type == 'cc_genrule':
1980            module_target.generated_headers.update(dep_module.genrule_headers)
1981            module_target.srcs.update(dep_module.genrule_srcs)
1982            module_target.shared_libs.update(dep_module.genrule_shared_libs)
1983            module_target.header_libs.update(dep_module.genrule_header_libs)
1984        elif dep_module.type in ['java_library', 'java_import']:
1985            if module.type not in ["cc_library_static"]:
1986                # This is needed to go around the case where `url` component depends
1987                # on `url_java`.
1988                # TODO(aymanm): Remove the if condition once crrev/4902547 has been imported downstream
1989                module_target.static_libs.add(dep_module.name)
1990        elif dep_module.type in ['genrule', 'java_genrule']:
1991            module_target.srcs.add(":" + dep_module.name)
1992        else:
1993            raise Exception(
1994                'Unsupported arch-specific dependency %s of target %s with type %s'
1995                % (dep_module.name, target.name, dep_module.type))
1996    return module
1997
1998
1999def turn_off_allocator_shim_for_musl(module):
2000    allocation_shim = "base/allocator/partition_allocator/shim/allocator_shim.cc"
2001    allocator_shim_files = {
2002        allocation_shim,
2003        "base/allocator/partition_allocator/shim/allocator_shim_default_dispatch_to_glibc.cc",
2004    }
2005    module.srcs -= allocator_shim_files
2006    for arch in module.target.values():
2007        arch.srcs -= allocator_shim_files
2008    module.target['android'].srcs.add(allocation_shim)
2009    if gn_utils.TESTING_SUFFIX in module.name:
2010        # allocator_shim_default_dispatch_to_glibc is only added to the __testing version of base
2011        # since base_base__testing is compiled for host. When compiling for host. Soong compiles
2012        # using glibc or musl(experimental). We currently only support compiling for glibc.
2013        module.target['glibc'].srcs.update(allocator_shim_files)
2014    else:
2015        # allocator_shim_default_dispatch_to_glibc does not exist in the prod version of base
2016        # `base_base` since this only compiles for android and bionic is used. Bionic is the equivalent
2017        # of glibc but for android.
2018        module.target['glibc'].srcs.add(allocation_shim)
2019
2020
2021def create_blueprint_for_targets(gn, targets, test_targets):
2022    """Generate a blueprint for a list of GN targets."""
2023    blueprint = Blueprint()
2024
2025    # Default settings used by all modules.
2026    defaults = Module('cc_defaults', defaults_module, '//gn:default_deps')
2027    defaults.cflags = [
2028        '-DGOOGLE_PROTOBUF_NO_RTTI',
2029        '-DBORINGSSL_SHARED_LIBRARY',
2030        '-Wno-error=return-type',
2031        '-Wno-non-virtual-dtor',
2032        '-Wno-macro-redefined',
2033        '-Wno-missing-field-initializers',
2034        '-Wno-sign-compare',
2035        '-Wno-sign-promo',
2036        '-Wno-unused-parameter',
2037        '-Wno-null-pointer-subtraction',  # Needed to libevent
2038        '-Wno-ambiguous-reversed-operator',  # needed for icui18n
2039        '-Wno-unreachable-code-loop-increment',  # needed for icui18n
2040        '-fPIC',
2041        '-Wno-c++11-narrowing',
2042    ]
2043    defaults.c_std = 'gnu11'
2044    # Chromium builds do not add a dependency for headers found inside the
2045    # sysroot, so they are added globally via defaults.
2046    defaults.target['android'].header_libs = [
2047        'jni_headers',
2048    ]
2049    defaults.target['android'].shared_libs = ['libmediandk']
2050    defaults.target['host'].cflags = [
2051        # -DANDROID is added by default but target.defines contain -DANDROID if
2052        # it's required.  So adding -UANDROID to cancel default -DANDROID if it's
2053        # not specified.
2054        # Note: -DANDROID is not consistently applied across the chromium code
2055        # base, so it is removed unconditionally for host targets.
2056        '-UANDROID',
2057    ]
2058    defaults.stl = 'none'
2059    defaults.cpp_std = 'c++17'
2060    defaults.min_sdk_version = 29
2061    defaults.apex_available.add(tethering_apex)
2062    blueprint.add_module(defaults)
2063
2064    for target in targets:
2065        module = create_modules_from_target(blueprint,
2066                                            gn,
2067                                            target,
2068                                            is_descendant_of_java=False,
2069                                            is_test_target=False)
2070        if module:
2071            module.visibility.update(root_modules_visibility)
2072
2073    for test_target in test_targets:
2074        module = create_modules_from_target(blueprint,
2075                                            gn,
2076                                            test_target +
2077                                            gn_utils.TESTING_SUFFIX,
2078                                            is_descendant_of_java=False,
2079                                            is_test_target=True)
2080        if module:
2081            module.visibility.update(root_modules_visibility)
2082
2083    # Merge in additional hardcoded arguments.
2084    for module in blueprint.modules.values():
2085        for key, add_val in additional_args.get(module.name, []):
2086            curr = getattr(module, key)
2087            if add_val and isinstance(add_val, set) and isinstance(curr, set):
2088                curr.update(add_val)
2089            elif isinstance(add_val, str) and (not curr
2090                                               or isinstance(curr, str)):
2091                setattr(module, key, add_val)
2092            elif isinstance(add_val, bool) and (not curr
2093                                                or isinstance(curr, bool)):
2094                setattr(module, key, add_val)
2095            elif isinstance(add_val, dict) and isinstance(curr, dict):
2096                curr.update(add_val)
2097            elif isinstance(add_val[1], dict) and isinstance(
2098                    curr[add_val[0]], Module.Target):
2099                curr[add_val[0]].__dict__.update(add_val[1])
2100            else:
2101                raise Exception(
2102                    'Unimplemented type %r of additional_args: %r' %
2103                    (type(add_val), key))
2104
2105    return blueprint
2106
2107
2108def create_package_module(blueprint):
2109    package = Module("package", "", "PACKAGE")
2110    package.comment = "The actual license can be found in Android.extras.bp"
2111    package.default_applicable_licenses.add(CRONET_LICENSE_NAME)
2112    package.default_visibility.append(package_default_visibility)
2113    blueprint.add_module(package)
2114
2115
2116def main():
2117    parser = argparse.ArgumentParser(
2118        description='Generate Android.bp from a GN description.')
2119    parser.add_argument(
2120        '--desc',
2121        help=
2122        'GN description (e.g., gn desc out --format=json --all-toolchains "//*".'
2123        + 'You can specify multiple --desc options for different target_cpu',
2124        required=True,
2125        action='append')
2126    parser.add_argument(
2127        '--extras',
2128        help='Extra targets to include at the end of the Blueprint file',
2129        default=os.path.join(gn_utils.repo_root(), 'Android.bp.extras'),
2130    )
2131    parser.add_argument(
2132        '--output',
2133        help='Blueprint file to create',
2134        default=os.path.join(gn_utils.repo_root(), 'Android.bp'),
2135    )
2136    parser.add_argument(
2137        '-v',
2138        '--verbose',
2139        help='Print debug logs.',
2140        action='store_true',
2141    )
2142    parser.add_argument(
2143        'targets',
2144        nargs=argparse.REMAINDER,
2145        help='Targets to include in the blueprint (e.g., "//:perfetto_tests")')
2146    args = parser.parse_args()
2147
2148    if args.verbose:
2149        log.basicConfig(format='%(levelname)s:%(funcName)s:%(message)s',
2150                        level=log.DEBUG)
2151
2152    targets = args.targets or DEFAULT_TARGETS
2153    gn = gn_utils.GnParser(builtin_deps)
2154    for desc_file in args.desc:
2155        with open(desc_file) as f:
2156            desc = json.load(f)
2157        for target in targets:
2158            gn.parse_gn_desc(desc, target)
2159        for test_target in DEFAULT_TESTS:
2160            gn.parse_gn_desc(desc, test_target, is_test_target=True)
2161    blueprint = create_blueprint_for_targets(gn, targets, DEFAULT_TESTS)
2162    project_root = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
2163    tool_name = os.path.relpath(os.path.abspath(__file__), project_root)
2164
2165    create_package_module(blueprint)
2166    output = [
2167        """// Copyright (C) 2022 The Android Open Source Project
2168//
2169// Licensed under the Apache License, Version 2.0 (the "License");
2170// you may not use this file except in compliance with the License.
2171// You may obtain a copy of the License at
2172//
2173//      http://www.apache.org/licenses/LICENSE-2.0
2174//
2175// Unless required by applicable law or agreed to in writing, software
2176// distributed under the License is distributed on an "AS IS" BASIS,
2177// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2178// See the License for the specific language governing permissions and
2179// limitations under the License.
2180//
2181// This file is automatically generated by %s. Do not edit.
2182
2183build = ["Android.extras.bp"]
2184""" % (tool_name)
2185    ]
2186    blueprint.to_string(output)
2187    if os.path.exists(args.extras):
2188        with open(args.extras, 'r') as r:
2189            for line in r:
2190                output.append(line.rstrip("\n\r"))
2191
2192    out_files = []
2193
2194    # Generate the Android.bp file.
2195    out_files.append(args.output + '.swp')
2196    with open(out_files[-1], 'w') as f:
2197        f.write('\n'.join(output))
2198        # Text files should have a trailing EOL.
2199        f.write('\n')
2200
2201    return 0
2202
2203
2204if __name__ == '__main__':
2205    sys.exit(main())
2206